gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/filesystem/siafile/encoding.go (about) 1 package siafile 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/json" 7 "fmt" 8 "io" 9 10 "gitlab.com/NebulousLabs/errors" 11 12 "gitlab.com/SkynetLabs/skyd/skymodules" 13 ) 14 15 // marshalChunk binary encodes a chunk. It only allocates memory a single time 16 // for the whole chunk. 17 func marshalChunk(chunk chunk) []byte { 18 chunkBytes := make([]byte, marshaledChunkSize(chunk.numPieces())) 19 buf := bytes.NewBuffer(chunkBytes) 20 21 // Write the extension info. 22 ei := buf.Next(len(chunk.ExtensionInfo)) 23 copy(ei, chunk.ExtensionInfo[:]) 24 25 // Write Stuck bool 26 stuck := buf.Next(1) 27 if chunk.Stuck { 28 copy(stuck, []byte{1}) 29 } else { 30 copy(stuck, []byte{0}) 31 } 32 33 // Write the pieces length prefix. 34 np := buf.Next(2) 35 binary.LittleEndian.PutUint16(np[:], uint16(chunk.numPieces())) 36 37 // Write the pieces. 38 for pieceIndex, pieceSet := range chunk.Pieces { 39 for _, piece := range pieceSet { 40 p := buf.Next(marshaledPieceSize) 41 putPiece(p, uint32(pieceIndex), piece) 42 } 43 } 44 return chunkBytes 45 } 46 47 // marshalErasureCoder marshals an erasure coder into its type and params. 48 func marshalErasureCoder(ec skymodules.ErasureCoder) ([4]byte, [8]byte) { 49 ecType := [4]byte(ec.Type()) 50 // Read params from ec. 51 ecParams := [8]byte{} 52 binary.LittleEndian.PutUint32(ecParams[:4], uint32(ec.MinPieces())) 53 binary.LittleEndian.PutUint32(ecParams[4:], uint32(ec.NumPieces()-ec.MinPieces())) 54 return ecType, ecParams 55 } 56 57 // marshalMetadata marshals the metadata of the SiaFile using json encoding. 58 func marshalMetadata(md Metadata) ([]byte, error) { 59 return json.Marshal(md) 60 } 61 62 // marshalPubKeyTable marshals the public key table of the SiaFile using Sia 63 // encoding. 64 func marshalPubKeyTable(pubKeyTable []HostPublicKey) ([]byte, error) { 65 // Create a buffer. 66 var buf bytes.Buffer 67 // Marshal all the data into the buffer 68 for _, pk := range pubKeyTable { 69 if err := pk.MarshalSia(&buf); err != nil { 70 return nil, err 71 } 72 } 73 return buf.Bytes(), nil 74 } 75 76 // numChunkPagesRequired calculates the number of pages on disk we need to 77 // reserve for each chunk to store numPieces. 78 func numChunkPagesRequired(numPieces int) uint8 { 79 chunkSize := marshaledChunkSize(numPieces) 80 numPages := chunkSize / pageSize 81 if chunkSize%pageSize != 0 { 82 numPages++ 83 } 84 return uint8(numPages) 85 } 86 87 // putPiece uses binary encoding to marshal a piece and puts the marshaled 88 // piece into out. That way when marshaling a chunk, the whole chunk's memory 89 // can be allocated with a single allocation. 90 func putPiece(out []byte, pieceIndex uint32, piece piece) { 91 binary.LittleEndian.PutUint32(out[:4], pieceIndex) 92 binary.LittleEndian.PutUint32(out[4:8], piece.HostTableOffset) 93 copy(out[8:], piece.MerkleRoot[:]) 94 } 95 96 // unmarshalChunk unmarshals a chunk which was previously marshaled using 97 // marshalChunk. It also requires the number of pieces as an argument to know 98 // how many unique pieces to expect when reading the pieces which we can easily 99 // find out by taking a look at the erasure coder within the siafile header. 100 // Unfortunately it's not enough to simply look at the piece indices when 101 // reading the pieces from disk, since there is no guarantee that we already 102 // uploaded a piece for each index. 103 func unmarshalChunk(numPieces uint32, raw []byte) (chunk chunk, err error) { 104 // initialize the pieces. 105 chunk.Pieces = make([][]piece, numPieces) 106 107 // read the ExtensionInfo first. 108 buf := bytes.NewBuffer(raw) 109 if _, err = io.ReadFull(buf, chunk.ExtensionInfo[:]); err != nil { 110 return chunk, errors.AddContext(err, "failed to unmarshal ExtensionInfo") 111 } 112 113 // read Stuck byte 114 stuckBytes := buf.Next(1) 115 chunk.Stuck = stuckBytes[0] == byte(1) 116 117 // read the pieces length prefix. 118 prefixLen := 2 119 prefixBytes := buf.Next(prefixLen) 120 if len(prefixBytes) != prefixLen { 121 return chunk, errors.New("length prefix missing") 122 } 123 piecesToLoad := binary.LittleEndian.Uint16(prefixBytes) 124 125 // read the pieces one by one. 126 var loadedPieces uint16 127 for pieceBytes := buf.Next(marshaledPieceSize); loadedPieces < piecesToLoad; pieceBytes = buf.Next(marshaledPieceSize) { 128 pieceIndex, piece, err := unmarshalPiece(pieceBytes) 129 if err != nil { 130 return chunk, err 131 } 132 if pieceIndex >= numPieces { 133 return chunk, fmt.Errorf("unexpected piece index, should be below %v but was %v", numPieces, pieceIndex) 134 } 135 chunk.Pieces[pieceIndex] = append(chunk.Pieces[pieceIndex], piece) 136 loadedPieces++ 137 } 138 return 139 } 140 141 // unmarshalErasureCoder unmarshals an ErasureCoder from the given params. 142 func unmarshalErasureCoder(ecType [4]byte, ecParams [8]byte) (skymodules.ErasureCoder, error) { 143 dataPieces := int(binary.LittleEndian.Uint32(ecParams[:4])) 144 parityPieces := int(binary.LittleEndian.Uint32(ecParams[4:])) 145 // Create correct erasure coder. 146 switch ecType { 147 case skymodules.ECReedSolomon: 148 return skymodules.NewRSCode(dataPieces, parityPieces) 149 case skymodules.ECReedSolomonSubShards64: 150 return skymodules.NewRSSubCode(dataPieces, parityPieces, 64) 151 default: 152 return nil, errors.New("unknown erasure code type") 153 } 154 } 155 156 // unmarshalMetadata unmarshals the json encoded metadata of the SiaFile. 157 func unmarshalMetadata(raw []byte) (md Metadata, err error) { 158 err = json.Unmarshal(raw, &md) 159 160 // We also need to create the erasure coder object. 161 md.staticErasureCode, err = unmarshalErasureCoder(md.StaticErasureCodeType, md.StaticErasureCodeParams) 162 if err != nil { 163 return 164 } 165 return 166 } 167 168 // unmarshalPiece unmarshals a piece from a byte slice which was previously 169 // marshaled using marshalPiece. 170 func unmarshalPiece(raw []byte) (pieceIndex uint32, piece piece, err error) { 171 if len(raw) != marshaledPieceSize { 172 err = fmt.Errorf("unexpected piece size, should be %v but was %v", marshaledPieceSize, len(raw)) 173 return 174 } 175 pieceIndex = binary.LittleEndian.Uint32(raw[:4]) 176 piece.HostTableOffset = binary.LittleEndian.Uint32(raw[4:8]) 177 copy(piece.MerkleRoot[:], raw[8:]) 178 return 179 } 180 181 // unmarshalPubKeyTable unmarshals a sia encoded public key table. 182 func unmarshalPubKeyTable(raw []byte) (keys []HostPublicKey, err error) { 183 // Create the buffer. 184 r := bytes.NewBuffer(raw) 185 // Unmarshal the keys one by one until EOF or a different error occur. 186 for { 187 var key HostPublicKey 188 if err = key.UnmarshalSia(r); errors.Contains(err, io.EOF) { 189 break 190 } else if err != nil { 191 return nil, err 192 } 193 keys = append(keys, key) 194 } 195 return keys, nil 196 }