github.com/scottcagno/storage@v1.8.0/pkg/lsmtree/readwrite.go (about) 1 package lsmtree 2 3 import ( 4 "encoding/binary" 5 "io" 6 ) 7 8 // readIndex reads and decodes the provided entry index from r 9 func readIndex(r io.Reader) (*Index, error) { 10 // make buffer 11 buf := make([]byte, 18) 12 // read index header 13 _, err := r.Read(buf) 14 if err != nil { 15 return nil, err 16 } 17 // decode key length 18 klen := binary.LittleEndian.Uint64(buf[0:8]) 19 // decode data offset 20 off, _ := binary.Varint(buf[8:18]) 21 // make entry index 22 i := &Index{ 23 Key: make([]byte, klen), 24 Offset: off, 25 } 26 // read key from data into entry key 27 _, err = r.Read(i.Key) 28 if err != nil { 29 return nil, err 30 } 31 // return index 32 return i, nil 33 } 34 35 // readIndexAt decodes the index at the provided offset using the provided reader 36 func readIndexAt(r io.ReaderAt, offset int64) (*Index, error) { 37 // make buffer 38 buf := make([]byte, 18) 39 // read index header 40 n, err := r.ReadAt(buf, offset) 41 if err != nil { 42 return nil, err 43 } 44 // update offset 45 offset += int64(n) 46 // decode key length 47 klen := binary.LittleEndian.Uint64(buf[0:8]) 48 // decode data offset 49 off, _ := binary.Varint(buf[8:18]) 50 // make entry index 51 e := &Index{ 52 Key: make([]byte, klen), 53 Offset: off, 54 } 55 // read key from data into entry key 56 n, err = r.ReadAt(e.Key, offset) 57 if err != nil { 58 return nil, err 59 } 60 // update offset 61 offset += int64(n) 62 // return entry 63 return e, nil 64 } 65 66 // writeIndex encodes and writes the provided entry index to w 67 func writeIndex(w io.WriteSeeker, i *Index) (int64, error) { 68 // error check 69 if i == nil { 70 return -1, ErrNilIndex 71 } 72 // get the file pointer offset for the entry 73 offset, err := w.Seek(0, io.SeekCurrent) 74 if err != nil { 75 return -1, err 76 } 77 // make buffer 78 buf := make([]byte, 18) 79 // encode and write index key length 80 binary.LittleEndian.PutUint64(buf[0:8], uint64(len(i.Key))) 81 _, err = w.Write(buf[0:8]) 82 if err != nil { 83 return -1, err 84 } 85 // encode and write index data offset 86 binary.PutVarint(buf[8:18], i.Offset) 87 _, err = w.Write(buf[8:18]) 88 if err != nil { 89 return -1, err 90 } 91 // write index key 92 _, err = w.Write(i.Key) 93 if err != nil { 94 return -1, err 95 } 96 return offset, nil 97 } 98 99 func readEntryHeader(r io.Reader, hdr *EntryHeader) (int, error) { 100 // make header buffer to read data into 101 buf := make([]byte, 16) 102 // read the header from the underlying reader into the buffer 103 n, err := r.Read(buf) 104 if err != nil { 105 return n, err 106 } 107 // decode key length 108 hdr.klen = binary.LittleEndian.Uint32(buf[0:4]) 109 // decode value length 110 hdr.vlen = binary.LittleEndian.Uint32(buf[4:8]) 111 // decode crc32 value 112 hdr.crc = binary.LittleEndian.Uint32(buf[8:12]) 113 // skip the last 4 bytes (reserved for future use) 114 // 115 return n, nil 116 } 117 118 // readEntry reads the entry from the provided io.Reader 119 // and returns the entry or nil and an error 120 func readEntry(r io.Reader) (*Entry, error) { 121 // make entry header 122 hdr := new(EntryHeader) 123 // reader entry header from r into EntryHeader 124 _, err := readEntryHeader(r, hdr) 125 if err != nil { 126 return nil, err 127 } 128 // make entry to read key and value into 129 e := &Entry{ 130 Key: make([]byte, hdr.klen), 131 Value: make([]byte, hdr.vlen), 132 CRC: hdr.crc, 133 } 134 // read key from data into entry key 135 _, err = r.Read(e.Key) 136 if err != nil { 137 return nil, err 138 } 139 // read value key from data into entry value 140 _, err = r.Read(e.Value) 141 if err != nil { 142 return nil, err 143 } 144 // make sure the crc checksum is valid 145 crc := checksum(append(e.Key, e.Value...)) 146 if e.CRC != crc { 147 return nil, ErrBadChecksum 148 } 149 // return entry 150 return e, nil 151 } 152 153 func readEntryHeaderAt(r io.ReaderAt, offset int64, hdr *EntryHeader) (int, error) { 154 // make header buffer to read data into 155 buf := make([]byte, 16) 156 // read the header from the underlying reader into the buffer 157 n, err := r.ReadAt(buf, offset) 158 if err != nil { 159 return n, err 160 } 161 // decode key length 162 hdr.klen = binary.LittleEndian.Uint32(buf[0:4]) 163 // decode value length 164 hdr.vlen = binary.LittleEndian.Uint32(buf[4:8]) 165 // decode crc32 value 166 hdr.crc = binary.LittleEndian.Uint32(buf[8:12]) 167 // skip the last 4 bytes (reserved for future use) 168 // 169 return n, nil 170 } 171 172 // readEntryAt reads the entry from the provided io.ReaderAt 173 // and returns the entry or nil and an error 174 func readEntryAt(r io.ReaderAt, offset int64) (*Entry, error) { 175 // make entry header 176 hdr := new(EntryHeader) 177 // reader entry header from r into EntryHeader 178 n, err := readEntryHeaderAt(r, offset, hdr) 179 if err != nil { 180 return nil, err 181 } 182 // update offset 183 offset += int64(n) 184 // make entry to read key and value into 185 e := &Entry{ 186 Key: make([]byte, hdr.klen), 187 Value: make([]byte, hdr.vlen), 188 CRC: hdr.crc, 189 } 190 // read key from data into entry key 191 n, err = r.ReadAt(e.Key, offset) 192 if err != nil { 193 return nil, err 194 } 195 // update offset 196 offset += int64(n) 197 // read value key from data into entry value 198 n, err = r.ReadAt(e.Value, offset) 199 if err != nil { 200 return nil, err 201 } 202 // update offset 203 offset += int64(n) 204 // make sure the crc checksum is valid 205 crc := checksum(append(e.Key, e.Value...)) 206 if e.CRC != crc { 207 return nil, ErrBadChecksum 208 } 209 // return entry 210 return e, nil 211 } 212 213 func writeEntryHeader(w io.Writer, hdr *EntryHeader) (int, error) { 214 // make header buffer to write data into 215 buf := make([]byte, 16) 216 // encode key length into header 217 binary.LittleEndian.PutUint32(buf[0:4], hdr.klen) 218 // encode value length into header 219 binary.LittleEndian.PutUint32(buf[4:8], hdr.vlen) 220 // encode crc32 value into header 221 binary.LittleEndian.PutUint32(buf[8:12], hdr.crc) 222 // skip the last 4 bytes (reserved for future use) 223 // 224 // write the header to the underlying writer 225 return w.Write(buf) 226 } 227 228 // writeEntry writes the provided entry to the provided io.Writer 229 func writeEntry(w io.WriteSeeker, e *Entry) (int64, error) { 230 // error check 231 if e == nil { 232 return -1, ErrNilEntry 233 } 234 // get the file pointer offset for the entry 235 offset, err := w.Seek(0, io.SeekCurrent) 236 if err != nil { 237 return -1, err 238 } 239 // make entry header 240 hdr := &EntryHeader{ 241 klen: uint32(len(e.Key)), 242 vlen: uint32(len(e.Value)), 243 crc: e.CRC, 244 } 245 // write entry header 246 _, err = writeEntryHeader(w, hdr) 247 if err != nil { 248 return -1, err 249 } 250 // write entry key 251 _, err = w.Write(e.Key) 252 if err != nil { 253 return -1, err 254 } 255 // write entry value 256 _, err = w.Write(e.Value) 257 if err != nil { 258 return -1, err 259 } 260 // return offset 261 return offset, nil 262 }