github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/seqid/seqid.gno (about)

     1  // Package seqid provides a simple way to have sequential IDs which will be
     2  // ordered correctly when inserted in an AVL tree.
     3  //
     4  // Sample usage:
     5  //
     6  //	var id seqid.ID
     7  //	var users avl.Tree
     8  //
     9  //	func NewUser() {
    10  //		users.Set(id.Next().String(), &User{ ... })
    11  //	}
    12  package seqid
    13  
    14  import (
    15  	"encoding/binary"
    16  
    17  	"gno.land/p/demo/cford32"
    18  )
    19  
    20  // An ID is a simple sequential ID generator.
    21  type ID uint64
    22  
    23  // Next advances the ID i.
    24  // It will panic if increasing ID would overflow.
    25  func (i *ID) Next() ID {
    26  	next, ok := i.TryNext()
    27  	if !ok {
    28  		panic("seqid: next ID overflows uint64")
    29  	}
    30  	return next
    31  }
    32  
    33  const maxID ID = 1<<64 - 1
    34  
    35  // TryNext increases i by 1 and returns its value.
    36  // It returns true if successful, or false if the increment would result in
    37  // an overflow.
    38  func (i *ID) TryNext() (ID, bool) {
    39  	if *i == maxID {
    40  		// Addition will overflow.
    41  		return 0, false
    42  	}
    43  	*i++
    44  	return *i, true
    45  }
    46  
    47  // Binary returns a big-endian binary representation of the ID,
    48  // suitable to be used as an AVL key.
    49  func (i ID) Binary() string {
    50  	buf := make([]byte, 8)
    51  	binary.BigEndian.PutUint64(buf, uint64(i))
    52  	return string(buf)
    53  }
    54  
    55  // String encodes i using cford32's compact encoding. For more information,
    56  // see the documentation for package [gno.land/p/demo/cford32].
    57  //
    58  // The result of String will be a 7-byte string for IDs [0,2^34), and a
    59  // 13-byte string for all values following that. All generated string IDs
    60  // follow the same lexicographic order as their number values; that is, for any
    61  // two IDs (x, y) such that x < y, x.String() < y.String().
    62  // As such, this string representation is suitable to be used as an AVL key.
    63  func (i ID) String() string {
    64  	return string(cford32.PutCompact(uint64(i)))
    65  }
    66  
    67  // FromBinary creates a new ID from the given string, expected to be a binary
    68  // big-endian encoding of an ID (such as that of [ID.Binary]).
    69  // The second return value is true if the conversion was successful.
    70  func FromBinary(b string) (ID, bool) {
    71  	if len(b) != 8 {
    72  		return 0, false
    73  	}
    74  	return ID(binary.BigEndian.Uint64([]byte(b))), true
    75  }
    76  
    77  // FromString creates a new ID from the given string, expected to be a string
    78  // representation using cford32, such as that returned by [ID.String].
    79  //
    80  // The encoding scheme used by cford32 allows the same ID to have many
    81  // different representations (though the one returned by [ID.String] is only
    82  // one, deterministic and safe to be used in AVL). The encoding scheme is
    83  // "human-centric" and is thus case insensitive, and maps some ambiguous
    84  // characters to be the same, ie. L = I = 1, O = 0. For this reason, when
    85  // parsing user input to retrieve a key (encoded as a string), always sanitize
    86  // it first using FromString, then run String(), instead of using the user's
    87  // input directly.
    88  func FromString(b string) (ID, error) {
    89  	n, err := cford32.Uint64([]byte(b))
    90  	return ID(n), err
    91  }