github.com/matrixorigin/matrixone@v0.7.0/pkg/objectio/reader.go (about) 1 // Copyright 2021 Matrix Origin 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 objectio 16 17 import ( 18 "context" 19 "github.com/matrixorigin/matrixone/pkg/logutil" 20 "io" 21 22 "github.com/matrixorigin/matrixone/pkg/common/mpool" 23 "github.com/matrixorigin/matrixone/pkg/compress" 24 25 "github.com/matrixorigin/matrixone/pkg/fileservice" 26 ) 27 28 type ObjectReader struct { 29 object *Object 30 name string 31 } 32 33 const ExtentTypeSize = 4 * 3 34 const ExtentsLength = 20 35 const FooterSize = 8 + 4 36 37 func NewObjectReader(name string, fs fileservice.FileService) (Reader, error) { 38 reader := &ObjectReader{ 39 name: name, 40 object: NewObject(name, fs), 41 } 42 return reader, nil 43 } 44 45 func (r *ObjectReader) ReadMeta(ctx context.Context, extents []Extent, m *mpool.MPool) ([]BlockObject, error) { 46 l := len(extents) 47 if l == 0 { 48 return nil, nil 49 } 50 51 metas := &fileservice.IOVector{ 52 FilePath: r.name, 53 Entries: make([]fileservice.IOEntry, 1, l), 54 } 55 56 metas.Entries[0] = fileservice.IOEntry{ 57 Offset: int64(extents[0].offset), 58 Size: int64(extents[0].originSize), 59 60 ToObject: func(reader io.Reader, data []byte) (any, int64, error) { 61 if len(data) == 0 { 62 var err error 63 data, err = io.ReadAll(reader) 64 if err != nil { 65 return nil, 0, err 66 } 67 } 68 dataLen := len(data) 69 blocks := make([]*Block, 0) 70 size := uint32(0) 71 i := uint32(0) 72 for { 73 if size == uint32(dataLen) { 74 break 75 } 76 extent := Extent{ 77 id: i, 78 offset: extents[0].offset, 79 length: extents[0].length, 80 originSize: extents[0].originSize, 81 } 82 block := &Block{ 83 object: r.object, 84 extent: extent, 85 name: r.name, 86 } 87 cache := data[size:dataLen] 88 unSize, err := block.UnMarshalMeta(cache) 89 if err != nil { 90 logutil.Infof("UnMarshalMeta failed: %v, extent %v", err.Error(), extents[0]) 91 return nil, 0, err 92 } 93 i++ 94 size += unSize 95 blocks = append(blocks, block) 96 } 97 return blocks, int64(len(data)), nil 98 }, 99 } 100 101 err := r.object.fs.Read(ctx, metas) 102 if err != nil { 103 return nil, err 104 } 105 106 blocks := make([]BlockObject, 0, l) 107 108 for _, extent := range extents { 109 block := metas.Entries[0].Object.([]*Block)[extent.id] 110 blocks = append(blocks, block) 111 } 112 return blocks, err 113 } 114 115 func (r *ObjectReader) Read(ctx context.Context, extent Extent, idxs []uint16, m *mpool.MPool) (*fileservice.IOVector, error) { 116 blocks, err := r.ReadMeta(ctx, []Extent{extent}, m) 117 if err != nil { 118 return nil, err 119 } 120 block := blocks[0] 121 122 data := &fileservice.IOVector{ 123 FilePath: r.name, 124 Entries: make([]fileservice.IOEntry, 0, len(idxs)), 125 } 126 for _, idx := range idxs { 127 col := block.(*Block).columns[idx] 128 data.Entries = append(data.Entries, fileservice.IOEntry{ 129 Offset: int64(col.GetMeta().location.Offset()), 130 Size: int64(col.GetMeta().location.Length()), 131 132 ToObject: newDecompressToObject(int64(col.GetMeta().location.OriginSize())), 133 }) 134 } 135 136 err = r.object.fs.Read(ctx, data) 137 if err != nil { 138 return nil, err 139 } 140 return data, nil 141 } 142 143 func (r *ObjectReader) ReadIndex(ctx context.Context, extent Extent, idxs []uint16, typ IndexDataType, m *mpool.MPool) ([]IndexData, error) { 144 blocks, err := r.ReadMeta(ctx, []Extent{extent}, m) 145 if err != nil { 146 return nil, err 147 } 148 block := blocks[0] 149 indexes := make([]IndexData, 0) 150 for _, idx := range idxs { 151 col := block.(*Block).columns[idx] 152 index, err := col.GetIndex(ctx, typ, m) 153 if err != nil { 154 return nil, err 155 } 156 indexes = append(indexes, index) 157 } 158 return indexes, nil 159 } 160 161 func (r *ObjectReader) ReadAllMeta(ctx context.Context, fileSize int64, m *mpool.MPool) ([]BlockObject, error) { 162 footer, err := r.readFooter(ctx, fileSize, m) 163 if err != nil { 164 return nil, err 165 } 166 return r.ReadMeta(ctx, footer.extents, m) 167 } 168 169 func (r *ObjectReader) readFooter(ctx context.Context, fileSize int64, m *mpool.MPool) (*Footer, error) { 170 var err error 171 var footer *Footer 172 173 // I don't know how many blocks there are in the object, 174 // read "ExtentsLength" blocks by default 175 size := int64(FooterSize + ExtentsLength*ExtentTypeSize) 176 if size > fileSize { 177 size = fileSize 178 } 179 footer, err = r.readFooterAndUnMarshal(ctx, fileSize, size, m) 180 if err != nil { 181 return nil, err 182 } 183 if len(footer.extents) == 0 { 184 size = int64(FooterSize + footer.blockCount*ExtentTypeSize) 185 footer, err = r.readFooterAndUnMarshal(ctx, fileSize, size, m) 186 if err != nil { 187 return nil, err 188 } 189 } 190 191 return footer, nil 192 } 193 194 func (r *ObjectReader) readFooterAndUnMarshal(ctx context.Context, fileSize, size int64, m *mpool.MPool) (*Footer, error) { 195 data := &fileservice.IOVector{ 196 FilePath: r.name, 197 Entries: []fileservice.IOEntry{ 198 { 199 Offset: fileSize - size, 200 Size: size, 201 202 ToObject: func(reader io.Reader, data []byte) (any, int64, error) { 203 // unmarshal 204 if len(data) == 0 { 205 var err error 206 data, err = io.ReadAll(reader) 207 if err != nil { 208 return nil, 0, err 209 } 210 } 211 footer := &Footer{} 212 if err := footer.UnMarshalFooter(data); err != nil { 213 return nil, 0, err 214 } 215 return footer, int64(len(data)), nil 216 }, 217 }, 218 }, 219 } 220 err := r.object.fs.Read(ctx, data) 221 if err != nil { 222 return nil, err 223 } 224 225 return data.Entries[0].Object.(*Footer), nil 226 } 227 228 type ToObjectFunc = func(r io.Reader, buf []byte) (any, int64, error) 229 230 // newDecompressToObject the decompression function passed to fileservice 231 func newDecompressToObject(size int64) ToObjectFunc { 232 return func(reader io.Reader, data []byte) (any, int64, error) { 233 // decompress 234 var err error 235 if len(data) == 0 { 236 data, err = io.ReadAll(reader) 237 if err != nil { 238 return nil, 0, err 239 } 240 } 241 decompressed := make([]byte, size) 242 decompressed, err = compress.Decompress(data, decompressed, compress.Lz4) 243 if err != nil { 244 return nil, 0, err 245 } 246 return decompressed, int64(len(decompressed)), nil 247 } 248 }