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  }