uplink/internal/metadata_injector.go

85 lines
1.9 KiB
Go
Raw Normal View History

2018-04-10 11:48:51 +00:00
package internal
import (
"fmt"
"io"
"math"
"strings"
)
func quote(text string) string {
text = strings.Replace(text, "\\", "\\\\", -1)
text = strings.Replace(text, "'", "\\'", -1)
text = "'" + text + "'"
return text
}
type MetadataInjector struct {
io.Reader
MetadataInterval int
blockOffset int
Metadata map[string]string
metadataBuf []byte
}
func NewMetadataInjector(r io.Reader, metadataInterval int) *MetadataInjector {
return &MetadataInjector{
Reader: r,
MetadataInterval: metadataInterval,
}
}
func (mi *MetadataInjector) generateMetadataBuf() {
mstr := ""
if mi.Metadata != nil {
for key, value := range mi.Metadata {
mstr += fmt.Sprintf("%s=%s;", key, quote(value))
}
}
if len(mstr) > 16*256-1 {
mstr = mstr[0 : 16*256]
}
lengthDiv := int(math.Ceil(float64(len(mstr)) / 16))
lengthByte := byte(lengthDiv)
mi.metadataBuf = make([]byte, lengthDiv*16+1)
mi.metadataBuf[0] = lengthByte
copy(mi.metadataBuf[1:], []byte(mstr))
}
func (mi *MetadataInjector) Read(data []byte) (n int, err error) {
if mi.metadataBuf != nil && len(mi.metadataBuf) > 0 {
bytesToRead := len(data)
if bytesToRead < len(mi.metadataBuf) {
// only read as much as possible
copy(data, mi.metadataBuf[0:bytesToRead])
n = bytesToRead
mi.metadataBuf = mi.metadataBuf[bytesToRead:]
return
}
// read everything
copy(data, mi.metadataBuf)
n = len(mi.metadataBuf)
mi.metadataBuf = nil
return
}
bytesToRead := mi.MetadataInterval - mi.blockOffset
if bytesToRead > len(data) {
bytesToRead = len(data)
}
if bytesToRead > 0 {
n, err = mi.Reader.Read(data[0:bytesToRead])
if err != nil {
return
}
mi.blockOffset += n
}
if mi.blockOffset == mi.MetadataInterval {
mi.generateMetadataBuf() // will be read in on next Read call
mi.blockOffset = 0
} else if mi.blockOffset > mi.MetadataInterval {
panic("block offset higher than metadata interval, logical error")
}
return
}