github.com/scottcagno/storage@v1.8.0/pkg/bio/_bio.go (about)

     1  package bio
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  )
     8  
     9  // working toy impl --> https://go.dev/play/p/q0Ww_3QPNAe
    10  // FINISHED PART --> https://go.dev/play/p/SkP9qNOdt7B
    11  
    12  var (
    13  	ErrInvalidSize   = errors.New("bio: invalid size")
    14  	ErrInvalidOffset = errors.New("bio: invalid offset")
    15  	ErrDataTooBig    = errors.New("bio: data too big")
    16  )
    17  
    18  const (
    19  	blockSize       = 32
    20  	headerSize      = 6
    21  	maxDataPerBlock = blockSize - headerSize
    22  )
    23  
    24  const (
    25  	blocksPerChunk  = 16
    26  	chunkSize       = blocksPerChunk * blockSize
    27  	maxDataPerChunk = blocksPerChunk * maxDataPerBlock
    28  )
    29  
    30  const (
    31  	chunksPerSegment = 15
    32  	segmentSize      = chunksPerSegment * chunkSize
    33  )
    34  
    35  const (
    36  	blockMask  = blockSize - 1
    37  	headerMask = headerSize - 1
    38  	chunkMask  = chunkSize - 1
    39  )
    40  
    41  const (
    42  	statusEmpty   = 0
    43  	statusActive  = 1
    44  	statusDeleted = 2
    45  	statusOther   = 3
    46  )
    47  
    48  const (
    49  	kindFull    = 1
    50  	kindBeg     = 2
    51  	kindMid     = 3
    52  	kindEnd     = 4
    53  	kindUnknown = 5
    54  )
    55  
    56  type header struct {
    57  	status uint8
    58  	kind   uint8
    59  	part   uint8
    60  	parts  uint8
    61  	length uint16
    62  }
    63  
    64  func (h *header) String() string {
    65  	return fmt.Sprintf("status=%d, kind=%d, part=%d, parts=%d, length=%d",
    66  		h.status, h.kind, h.part, h.parts, h.length)
    67  }
    68  
    69  func getKind(part, parts int) uint8 {
    70  	if parts == 1 {
    71  		return kindFull
    72  	}
    73  	if part == 1 {
    74  		return kindBeg
    75  	}
    76  	if part > 1 && part < parts {
    77  		return kindMid
    78  	}
    79  	if part == parts {
    80  		return kindEnd
    81  	}
    82  	return kindUnknown
    83  }
    84  
    85  func (h *header) Write(p []byte) (int, error) {
    86  	if p == nil || len(p) != 6 {
    87  		return -1, errors.New("bad size")
    88  	}
    89  	_ = p[5]
    90  	p[0] = h.status
    91  	p[1] = h.kind
    92  	p[2] = h.part
    93  	p[3] = h.parts
    94  	p[4] = byte(h.length)
    95  	p[5] = byte(h.length >> 8)
    96  	return len(p), nil
    97  }
    98  
    99  func encodeHeader(p []byte, h *header) (int, error) {
   100  	if p == nil || len(p) != 6 {
   101  		return -1, ErrInvalidSize
   102  	}
   103  	if h == nil {
   104  		// encode "zero value" header
   105  		h = new(header)
   106  		h.status = statusEmpty
   107  		h.kind = kindFull
   108  		h.part = 1
   109  		h.parts = 1
   110  		h.length = 0
   111  	}
   112  	_ = p[5]
   113  	p[0] = h.status
   114  	p[1] = h.kind
   115  	p[2] = h.part
   116  	p[3] = h.parts
   117  	p[4] = byte(h.length)
   118  	p[5] = byte(h.length >> 8)
   119  	return len(p), nil
   120  }
   121  
   122  func (h *header) WriteTo(w io.Writer) (int64, error) {
   123  	n, err := w.Write([]byte{
   124  		h.status,
   125  		h.kind,
   126  		h.part,
   127  		h.parts,
   128  		byte(h.length),
   129  		byte(h.length >> 8),
   130  	})
   131  	if err != nil {
   132  		return -1, err
   133  	}
   134  	return int64(n), nil
   135  }
   136  
   137  func (h *header) Read(p []byte) (int, error) {
   138  	if p == nil || len(p) != 6 {
   139  		return -1, errors.New("bad size")
   140  	}
   141  	_ = p[5]
   142  	h.status = p[0]
   143  	h.kind = p[1]
   144  	h.part = p[2]
   145  	h.parts = p[3]
   146  	h.length = uint16(p[4]) | uint16(p[5])<<8
   147  	return len(p), nil
   148  }
   149  
   150  func decodeHeader(p []byte, h *header) (int, error) {
   151  	if p == nil || len(p) != 6 {
   152  		return -1, ErrInvalidSize
   153  	}
   154  	_ = p[5]
   155  	h.status = p[0]
   156  	h.kind = p[1]
   157  	h.part = p[2]
   158  	h.parts = p[3]
   159  	h.length = uint16(p[4]) | uint16(p[5])<<8
   160  	return len(p), nil
   161  }
   162  
   163  func allocate(size int) []byte {
   164  	return calloc(align(size, size-1))
   165  }
   166  
   167  func info(p *[]byte) {
   168  	if p == nil {
   169  		fmt.Println("ptr=nil, len=0, cap=0, data=nil")
   170  	}
   171  	fmt.Printf("ptr=%p, len=%d, cap=%d, data=%q\n", *p, len(*p), cap(*p), *p)
   172  }
   173  
   174  func longStr(s string, pre string, max int) string {
   175  	var ss string
   176  	for i := 0; i < len(s); i += max {
   177  		j := i + max
   178  		if j > len(s) {
   179  			j = len(s)
   180  		}
   181  		fmtr := fmt.Sprintf("%s| %%-%ds |\n", pre, max)
   182  		ss += fmt.Sprintf(fmtr, s[i:j])
   183  	}
   184  	return ss
   185  }
   186  
   187  func divUp(dividend, divisor int) int {
   188  	// divide
   189  	res := dividend / divisor
   190  	// divided evenly
   191  	if (dividend % divisor) == 0 {
   192  		return res
   193  	}
   194  	// rounded down
   195  	if (divisor ^ dividend) >= 0 {
   196  		return res + 1
   197  	}
   198  	return res
   199  }
   200  
   201  func ChunkSliceIter(slice []int, chunkSize int, fn func(p []int) int) {
   202  	for beg := 0; beg < len(slice); beg += chunkSize {
   203  		end := beg + chunkSize
   204  		// necessary check to avoid slicing beyond
   205  		// slice capacity
   206  		if end > len(slice) {
   207  			end = len(slice)
   208  		}
   209  		n := fn(slice[beg:end])
   210  		_ = n
   211  	}
   212  }
   213  
   214  // this impl does not continuously modify the slice, and uses iteration
   215  func ChunkSliceV1(slice []int, chunkSize int) [][]int {
   216  	var chunks [][]int
   217  	for i := 0; i < len(slice); i += chunkSize {
   218  		end := i + chunkSize
   219  		// necessary check to avoid slicing beyond
   220  		// slice capacity
   221  		if end > len(slice) {
   222  			end = len(slice)
   223  		}
   224  		chunks = append(chunks, slice[i:end])
   225  	}
   226  	return chunks
   227  }
   228  
   229  // this impl continuously modifies the slice and calls break eventually
   230  func ChunkSliceV2(slice []int, chunkSize int) [][]int {
   231  	var chunks [][]int
   232  	for {
   233  		if len(slice) == 0 {
   234  			break
   235  		}
   236  		// necessary check to avoid slicing beyond
   237  		// slice capacity
   238  		if len(slice) < chunkSize {
   239  			chunkSize = len(slice)
   240  		}
   241  		chunks = append(chunks, slice[0:chunkSize])
   242  		slice = slice[chunkSize:]
   243  	}
   244  	return chunks
   245  }
   246  
   247  func calcBlocks(size int) int {
   248  	size = align(size, blockMask)
   249  	return size / (blockSize - headerSize)
   250  }
   251  
   252  func align(size, mask int) int {
   253  	return (size + mask) &^ (mask)
   254  }
   255  
   256  func calloc(size int) []byte {
   257  	return make([]byte, size, size)
   258  }
   259  
   260  func malloc(size int) []byte {
   261  	return make([]byte, 0, size)
   262  }
   263  
   264  func clear(p *[]byte) (int, int) {
   265  	*p = (*p)[:0]
   266  	return len(*p), cap(*p)
   267  }
   268  
   269  func free(p *[]byte) {
   270  	*p = nil
   271  }