85 lines
1.9 KiB
Go
85 lines
1.9 KiB
Go
|
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
|
||
|
}
|