github.com/coreos/mantle@v0.13.0/network/journal/export.go (about) 1 // Copyright 2017 CoreOS, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package journal 16 17 import ( 18 "bufio" 19 "bytes" 20 "encoding/binary" 21 "errors" 22 "io" 23 ) 24 25 type ExportReader struct { 26 buf *bufio.Reader 27 } 28 29 func NewExportReader(r io.Reader) *ExportReader { 30 return &ExportReader{ 31 buf: bufio.NewReader(r), 32 } 33 } 34 35 // ReadEntry reads one journal entry from the stream and returns it as a map. 36 func (e *ExportReader) ReadEntry() (Entry, error) { 37 entry := make(Entry) 38 for { 39 name, value, err := e.readField() 40 if err != nil { 41 return nil, err 42 } 43 if name == "" { 44 if len(entry) != 0 { 45 // terminate entry on a trailing newline. 46 return entry, nil 47 } 48 // skip any leading newlines. 49 continue 50 } 51 entry[name] = value 52 } 53 } 54 55 // read a text or binary field name and value. 56 func (e *ExportReader) readField() (name string, value []byte, err error) { 57 line, err := e.readLine() 58 if err != nil { 59 return 60 } 61 if len(line) == 0 { 62 return 63 } 64 65 eq := bytes.IndexByte(line, '=') 66 if eq == 0 { 67 err = errors.New("journal: empty field name") 68 return 69 } else if eq > 0 { 70 name = string(line[:eq]) 71 value = line[eq+1:] 72 return 73 } else { 74 name = string(line) 75 value, err = e.readBinary() 76 return 77 } 78 } 79 80 // read the next line, trim the trailing newline. 81 func (e *ExportReader) readLine() ([]byte, error) { 82 line, err := e.buf.ReadBytes('\n') 83 if err != nil { 84 return nil, err 85 } 86 // trim the trailing newline 87 return line[:len(line)-1], nil 88 } 89 90 // read binary field value 91 func (e *ExportReader) readBinary() ([]byte, error) { 92 // first, a little-endian 64bit data size 93 size := make([]byte, 8) 94 if _, err := io.ReadFull(e.buf, size); err != nil { 95 return nil, err 96 } 97 98 // then, the data 99 value := make([]byte, binary.LittleEndian.Uint64(size)) 100 if _, err := io.ReadFull(e.buf, value); err != nil { 101 return nil, err 102 } 103 104 // finally, a trailing newline before the next field. 105 if newline, err := e.buf.ReadByte(); err != nil { 106 return nil, err 107 } else if newline != '\n' { 108 return nil, errors.New("journal: binary field missing terminating newline") 109 } 110 111 return value, nil 112 }