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 }