github.com/leg100/ots@v0.0.7-0.20210919080622-034055ced4bd/blob.go (about)

     1  package ots
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/google/uuid"
     7  )
     8  
     9  const (
    10  	// ChunkMaxLimit is maximum permissible size of a chunk
    11  	ChunkMaxLimit = 65536
    12  
    13  	// ChunkStartMarker is the special byte that prefixes the first chunk
    14  	ChunkStartMarker = byte(2)
    15  
    16  	// ChunkEndMarker is the special byte that suffixes the last chunk
    17  	ChunkEndMarker = byte(3)
    18  )
    19  
    20  // BlobStore implementations provide a persistent store from and to which binary
    21  // objects can be fetched and uploaded.
    22  type BlobStore interface {
    23  	// Get fetches a blob
    24  	Get(string) ([]byte, error)
    25  
    26  	// Get fetches a blob chunk
    27  	GetChunk(string, GetChunkOptions) ([]byte, error)
    28  
    29  	// Put uploads a blob
    30  	Put(string, []byte) error
    31  
    32  	// Put uploads a blob chunk
    33  	PutChunk(string, []byte, PutChunkOptions) error
    34  }
    35  
    36  type GetChunkOptions struct {
    37  	// The maximum number of bytes of logs to return to the client
    38  	Limit int `schema:"limit"`
    39  
    40  	// The start position in the logs from which to send to the client
    41  	Offset int `schema:"offset"`
    42  }
    43  
    44  type PutChunkOptions struct {
    45  	// End indicates this is the last and final chunk
    46  	End bool `schema:"end"`
    47  }
    48  
    49  // NewBlobID generates a unique blob ID
    50  func NewBlobID() string {
    51  	return uuid.NewString()
    52  }
    53  
    54  // GetChunk retrieves a chunk of bytes from a byte slice. The first chunk in the
    55  // slice is prefixed with a special byte. If complete is true then the last
    56  // chunk in the slice is suffixed with a special byte.
    57  func GetChunk(p []byte, opts GetChunkOptions, complete bool) ([]byte, error) {
    58  	if opts.Offset == 0 {
    59  		p = append([]byte{ChunkStartMarker}, p...)
    60  	}
    61  
    62  	if complete {
    63  		p = append(p, ChunkEndMarker)
    64  	}
    65  
    66  	if opts.Offset > len(p) {
    67  		return nil, fmt.Errorf("offset greater than size of binary object")
    68  	}
    69  
    70  	if opts.Limit > ChunkMaxLimit {
    71  		opts.Limit = ChunkMaxLimit
    72  	}
    73  
    74  	// Adjust limit if it extends beyond size of binary object
    75  	if (opts.Offset + opts.Limit) > len(p) {
    76  		opts.Limit = len(p) - opts.Offset
    77  	}
    78  
    79  	return p[opts.Offset:(opts.Offset + opts.Limit)], nil
    80  }