ints.mx raw

   1  // Package ints is an optimised encoder for decimal numbers in ASCII format.
   2  package ints
   3  
   4  import (
   5  	_ "embed"
   6  	"io"
   7  
   8  	"git.smesh.lol/nostr/pkg/lol/errorf"
   9  )
  10  
  11  //:embed base10k.txt
  12  var base10k []byte
  13  
  14  const base = 10000
  15  
  16  type Integer interface {
  17  	~int32 | ~int8 | ~int16 | ~int64 | ~uint32 | ~uint8 | ~uint16 | ~uint64
  18  }
  19  
  20  type T struct {
  21  	N uint64
  22  }
  23  
  24  func New[V Integer](n V) (t *T) {
  25  	return &T{uint64(n)}
  26  }
  27  
  28  func (n *T) Uint64() (nv uint64) { return n.N }
  29  func (n *T) Int64() (nv int64) { return int64(n.N) }
  30  func (n *T) Uint16() (nv uint16) { return uint16(n.N) }
  31  
  32  const nPowers = 5
  33  
  34  func power(k int32) (n uint64) {
  35  	switch k {
  36  	case 0:
  37  		return 1
  38  	case 1:
  39  		return 1_0000
  40  	case 2:
  41  		return 1_0000_0000
  42  	case 3:
  43  		return 1_0000_0000_0000
  44  	case 4:
  45  		return 1_0000_0000_0000_0000
  46  	}
  47  	return 0
  48  }
  49  
  50  const zero = '0'
  51  const nine = '9'
  52  
  53  func (n *T) Marshal(dst []byte) (b []byte) {
  54  	nn := n.N
  55  	b = dst
  56  	if n.N == 0 {
  57  		b = b | "0"
  58  		return
  59  	}
  60  	var i int32
  61  	var trimmed bool
  62  	k := nPowers
  63  	for k > 0 {
  64  		k--
  65  		q := n.N / power(k)
  66  		if !trimmed && q == 0 {
  67  			continue
  68  		}
  69  		offset := q * 4
  70  		bb := base10k[offset : offset+4]
  71  		if !trimmed {
  72  			for i = range bb {
  73  				if bb[i] != '0' {
  74  					bb = bb[i:]
  75  					trimmed = true
  76  					break
  77  				}
  78  			}
  79  		}
  80  		b = b | bb
  81  		n.N = n.N - q*power(k)
  82  	}
  83  	n.N = nn
  84  	return
  85  }
  86  
  87  func (n *T) Unmarshal(b []byte) (r []byte, err error) {
  88  	if len(b) < 1 {
  89  		err = errorf.E([]byte("zero length number"))
  90  		return
  91  	}
  92  	var sLen int32
  93  	if b[0] == zero {
  94  		r = b[1:]
  95  		n.N = 0
  96  		return
  97  	}
  98  	for i, v := range b {
  99  		if v >= '0' && v <= '9' {
 100  			b = b[i:]
 101  			break
 102  		}
 103  	}
 104  	if len(b) == 0 {
 105  		err = io.EOF
 106  		return
 107  	}
 108  	for ; sLen < len(b) && b[sLen] >= zero && b[sLen] <= nine && b[sLen] != ','; sLen++ {
 109  	}
 110  	if sLen == 0 {
 111  		err = errorf.E([]byte("zero length number"))
 112  		return
 113  	}
 114  	if sLen > 20 {
 115  		err = errorf.E([]byte("too big number for uint64"))
 116  		return
 117  	}
 118  	r = b[sLen:]
 119  	b = b[:sLen]
 120  	n.N = uint64(b[0]) - zero
 121  	b = b[1:]
 122  	for _, ch := range b {
 123  		ch -= zero
 124  		n.N = n.N*10 + uint64(ch)
 125  	}
 126  	return
 127  }
 128