github.com/git-amp/amp-sdk-go@v0.7.5/amp/support.core.go (about)

     1  package amp
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"strings"
     7  	"time"
     8  	"unsafe"
     9  
    10  	"github.com/git-amp/amp-sdk-go/stdlib/bufs"
    11  )
    12  
    13  // URI form of a glyph typically followed by a media (mime) type.
    14  const GlyphURIPrefix = "amp:glyph/"
    15  
    16  // Describes an asset to be an image stream but not specify format / codec
    17  var GenericImageType = "image/*"
    18  
    19  // UTC16 is a signed UTC timestamp, storing the elapsed 1/65536 second ticks since Jan 1, 1970 UTC.
    20  //
    21  // Shifting this value to the right 16 bits will yield standard Unix time.
    22  // This means there are 47 bits dedicated for seconds, implying a limit 4.4 million years.
    23  type UTC16 int64
    24  
    25  // TID identifies a specific planet, node, or transaction.
    26  //
    27  // Unless otherwise specified a TID in the wild should always be considered read-only.
    28  type TID []byte
    29  
    30  // TIDBuf is embedded UTC16 value followed by a 24 byte hash.
    31  type TIDBuf [TIDBinaryLen]byte
    32  
    33  // Byte size of a TID, a hash with a leading embedded big endian binary time index.
    34  const TIDBinaryLen = int(Const_TIDBinaryLen)
    35  
    36  // ASCII string length of a CellTID encoded into its base32 form.
    37  const TIDStringLen = int(Const_TIDStringLen)
    38  
    39  // nilTID is a zeroed TID that denotes a void/nil/zero value of a TID
    40  var nilTID = TID{}
    41  
    42  const (
    43  	SI_DistantFuture = UTC16(0x7FFFFFFFFFFFFFFF)
    44  )
    45  
    46  // Converts a time.Time to a UTC16.
    47  func ConvertToUTC16(t time.Time) UTC16 {
    48  	time16 := t.Unix() << 16
    49  	frac := uint16((2199 * (uint32(t.Nanosecond()) >> 10)) >> 15)
    50  	return UTC16(time16 | int64(frac))
    51  }
    52  
    53  // Similar to time.Now() but returns a UTC16.
    54  func Now() UTC16 {
    55  	return ConvertToUTC16(time.Now())
    56  }
    57  
    58  // Converts milliseconds to UTC16.
    59  func ConvertMsToUTC(ms int64) UTC16 {
    60  	return UTC16((ms << 16) / 1000)
    61  }
    62  
    63  // Converts UTC16 to a time.Time.
    64  func (t UTC16) ToTime() time.Time {
    65  	return time.Unix(int64(t>>16), int64(t&0xFFFF)*15259)
    66  }
    67  
    68  // Converts UTC16 to milliseconds.
    69  func (t UTC16) ToMs() int64 {
    70  	return (int64(t>>8) * 1000) >> 8
    71  }
    72  
    73  // TID is a convenience function that returns the TID contained within this TxID.
    74  func (tid *TIDBuf) TID() TID {
    75  	return tid[:]
    76  }
    77  
    78  // Base32 returns this TID in Base32 form.
    79  func (tid *TIDBuf) Base32() string {
    80  	return bufs.Base32Encoding.EncodeToString(tid[:])
    81  }
    82  
    83  // IsNil returns true if this TID length is 0 or is equal to NilTID
    84  func (tid TID) IsNil() bool {
    85  	if len(tid) == 0 {
    86  		return true
    87  	}
    88  
    89  	if bytes.Equal(tid, nilTID[:]) {
    90  		return true
    91  	}
    92  
    93  	return false
    94  }
    95  
    96  // Clone returns a duplicate of this TID
    97  func (tid TID) Clone() TID {
    98  	dupe := make([]byte, len(tid))
    99  	copy(dupe, tid)
   100  	return dupe
   101  }
   102  
   103  // Buf is a convenience function that make a new TxID from a TID byte slice.
   104  func (tid TID) Buf() (buf TIDBuf) {
   105  	copy(buf[:], tid)
   106  	return buf
   107  }
   108  
   109  // Base32 returns this TID in Base32 form.
   110  func (tid TID) Base32() string {
   111  	return bufs.Base32Encoding.EncodeToString(tid)
   112  }
   113  
   114  // Appends the base 32 ASCII encoding of this TID to the given buffer
   115  func (tid TID) AppendAsBase32(in []byte) []byte {
   116  	encLen := bufs.Base32Encoding.EncodedLen(len(tid))
   117  	needed := len(in) + encLen
   118  	buf := in
   119  	if needed > cap(buf) {
   120  		buf = make([]byte, (needed+0x100)&^0xFF)
   121  		buf = append(buf[:0], in...)
   122  	}
   123  	buf = buf[:needed]
   124  	bufs.Base32Encoding.Encode(buf[len(in):needed], tid)
   125  	return buf
   126  }
   127  
   128  // SuffixStr returns the last few digits of this TID in string form (for easy reading, logs, etc)
   129  func (tid TID) SuffixStr() string {
   130  	const summaryStrLen = 5
   131  
   132  	R := len(tid)
   133  	L := R - summaryStrLen
   134  	if L < 0 {
   135  		L = 0
   136  	}
   137  	return bufs.Base32Encoding.EncodeToString(tid[L:R])
   138  }
   139  
   140  // SetTimeAndHash writes the given timestamp and the right-most part of inSig into this TID.
   141  //
   142  // See comments for TIDBinaryLen
   143  func (tid TID) SetTimeAndHash(time UTC16, hash []byte) {
   144  	tid.SetUTC(time)
   145  	tid.SetHash(hash)
   146  }
   147  
   148  // SetHash sets the sig/hash portion of this ID
   149  func (tid TID) SetHash(hash []byte) {
   150  	const TIDHashSz = int(Const_TIDBinaryLen - 8)
   151  	pos := len(hash) - TIDHashSz
   152  	if pos >= 0 {
   153  		copy(tid[TIDHashSz:], hash[pos:])
   154  	} else {
   155  		for i := 8; i < int(Const_TIDBinaryLen); i++ {
   156  			tid[i] = hash[i]
   157  		}
   158  	}
   159  }
   160  
   161  // SetUTC16 writes the given UTC16 into this TID
   162  func (tid TID) SetUTC(t UTC16) {
   163  	tid[0] = byte(t >> 56)
   164  	tid[1] = byte(t >> 48)
   165  	tid[2] = byte(t >> 40)
   166  	tid[3] = byte(t >> 32)
   167  	tid[4] = byte(t >> 24)
   168  	tid[5] = byte(t >> 16)
   169  	tid[6] = byte(t >> 8)
   170  	tid[7] = byte(t)
   171  }
   172  
   173  // ExtractUTC16 returns the unix timestamp embedded in this TID (a unix timestamp in 1<<16 seconds UTC)
   174  func (tid TID) ExtractUTC() UTC16 {
   175  	t := int64(tid[0])
   176  	t = (t << 8) | int64(tid[1])
   177  	t = (t << 8) | int64(tid[2])
   178  	t = (t << 8) | int64(tid[3])
   179  	t = (t << 8) | int64(tid[4])
   180  	t = (t << 8) | int64(tid[5])
   181  	t = (t << 8) | int64(tid[6])
   182  	t = (t << 8) | int64(tid[7])
   183  
   184  	return UTC16(t)
   185  }
   186  
   187  // ExtractTime returns the unix timestamp embedded in this TID (a unix timestamp in seconds UTC)
   188  func (tid TID) ExtractTime() int64 {
   189  	t := int64(tid[0])
   190  	t = (t << 8) | int64(tid[1])
   191  	t = (t << 8) | int64(tid[2])
   192  	t = (t << 8) | int64(tid[3])
   193  	t = (t << 8) | int64(tid[4])
   194  	t = (t << 8) | int64(tid[5])
   195  
   196  	return t
   197  }
   198  
   199  // SelectEarlier looks in inTime a chooses whichever is earlier.
   200  //
   201  // If t is later than the time embedded in this TID, then this function has no effect and returns false.
   202  //
   203  // If t is earlier, then this TID is initialized to t (and the rest zeroed out) and returns true.
   204  func (tid TID) SelectEarlier(t UTC16) bool {
   205  
   206  	TIDt := tid.ExtractUTC()
   207  
   208  	// Timestamp of 0 is reserved and should only reflect an invalid/uninitialized TID.
   209  	if t < 0 {
   210  		t = 0
   211  	}
   212  
   213  	if t < TIDt || t == 0 {
   214  		tid.SetUTC(t)
   215  		for i := 8; i < len(tid); i++ {
   216  			tid[i] = 0
   217  		}
   218  		return true
   219  	}
   220  
   221  	return false
   222  }
   223  
   224  // CopyNext copies the given TID and increments it by 1, typically useful for seeking the next entry after a given one.
   225  func (tid TID) CopyNext(inTID TID) {
   226  	copy(tid, inTID)
   227  	for j := len(tid) - 1; j > 0; j-- {
   228  		tid[j]++
   229  		if tid[j] > 0 {
   230  			break
   231  		}
   232  	}
   233  }
   234  
   235  // CellID is unique Cell identifier that globally identifies a cell.
   236  type CellID [16]byte
   237  
   238  // Forms a CellID from uint64s.
   239  func CellIDFromU64(x0, x1 uint64) (id CellID) {
   240  	id.AssignFromU64(x0, x1)
   241  	return id
   242  }
   243  
   244  func (id *CellID) IsNil() bool {
   245  	return *(*int64)(unsafe.Pointer(id)) == 0 && *(*int64)(unsafe.Pointer(&id[8])) == 0
   246  }
   247  
   248  func (id *CellID) AssignFromU64(x0, x1 uint64) {
   249  	binary.BigEndian.PutUint64(id[0:8], x0)
   250  	binary.BigEndian.PutUint64(id[8:16], x1)
   251  }
   252  
   253  func (id *CellID) ExportAsU64() (x0, x1 uint64) {
   254  	x0 = binary.BigEndian.Uint64(id[0:8])
   255  	x1 = binary.BigEndian.Uint64(id[8:16])
   256  	return
   257  }
   258  
   259  func (id *CellID) String() string {
   260  	return bufs.Base32Encoding.EncodeToString(id[:])
   261  }
   262  
   263  /*
   264  // Issues a CellID using the given random number generator to generate the UID hash portion.
   265  func IssueCellID(rng *rand.Rand) (id CellID) {
   266  	return CellIDFromU64(uint64(ConvertToUTC16(time.Now())), rng.Uint64())
   267  }
   268  
   269  // TID identifies a Tx (or Cell) by secure hash ID.
   270  type TxID struct {
   271  	UTC16 UTC16
   272  	Hash1 uint64
   273  	Hash2 uint64
   274  	Hash3 uint64
   275  }
   276  
   277  // Base32 returns this TID in Base32 form.
   278  func (tid *TxID) Base32() string {
   279  	var bin [TIDBinaryLen]byte
   280  	binStr := tid.AppendAsBinary(bin[:0])
   281  	return bufs.Base32Encoding.EncodeToString(binStr)
   282  }
   283  
   284  // Appends the base 32 ASCII encoding of this TID to the given buffer
   285  func (tid *TxID) AppendAsBase32(io []byte) []byte {
   286  	L := len(io)
   287  
   288  	needed := L + TIDStringLen
   289  	dst := io
   290  	if needed > cap(dst) {
   291  		dst = make([]byte, (needed+0x100)&^0xFF)
   292  		dst = append(dst[:0], io...)
   293  	}
   294  	dst = dst[:needed]
   295  
   296  	var bin [TIDBinaryLen]byte
   297  	binStr := tid.AppendAsBinary(bin[:0])
   298  
   299  	bufs.Base32Encoding.Encode(dst[L:needed], binStr)
   300  	return dst
   301  }
   302  
   303  // Appends the base 32 ASCII encoding of this TID to the given buffer
   304  func (tid *TxID) AppendAsBinary(io []byte) []byte {
   305  	L := len(io)
   306  	needed := L + TIDBinaryLen
   307  	dst := io
   308  	if needed > cap(dst) {
   309  		dst = make([]byte, needed)
   310  		dst = append(dst[:0], io...)
   311  	}
   312  	dst = dst[:needed]
   313  
   314  	binary.BigEndian.PutUint64(dst[L+0:L+8], uint64(tid.UTC16))
   315  	binary.BigEndian.PutUint64(dst[L+8:L+16], tid.Hash1)
   316  	binary.BigEndian.PutUint64(dst[L+16:L+24], tid.Hash2)
   317  	binary.BigEndian.PutUint64(dst[L+24:L+32], tid.Hash3)
   318  	return dst
   319  }
   320  */
   321  
   322  func (id ConstSymbol) Ord() uint32 {
   323  	return uint32(id)
   324  }
   325  
   326  // Analyses an AttrSpec's SeriesSpec and returns the index class it uses.
   327  func GetSeriesIndexType(seriesSpec string) SeriesIndexType {
   328  	switch {
   329  	case strings.HasSuffix(seriesSpec, ".Name"):
   330  		return SeriesIndexType_Name
   331  	default:
   332  		return SeriesIndexType_Literal
   333  	}
   334  }
   335  
   336  func (params *PinReqParams) URLPath() []string {
   337  	if params.URL == nil {
   338  		return nil
   339  	}
   340  	path := params.URL.Path
   341  	if path != "" && path[0] == '/' {
   342  		path = path[1:]
   343  	}
   344  	return strings.Split(path, "/")
   345  }
   346  
   347  func (params *PinReqParams) Params() *PinReqParams {
   348  	return params
   349  }
   350  
   351  /*
   352  
   353  // ReadCell loads a cell with the given URI having the inferred schema (built from its fields using reflection).
   354  // The URI is scoped into the user's home planet and AppID.
   355  func ReadCell(ctx AppContext, subKey string, schema *AttrSchema, dstStruct any) error {
   356  
   357  	dst := reflect.Indirect(reflect.ValueOf(dstStruct))
   358  	switch dst.Kind() {
   359  	case reflect.Pointer:
   360  		dst = dst.Elem()
   361  	case reflect.Struct:
   362  	default:
   363  		return ErrCode_ExportErr.Errorf("expected struct, got %v", dst.Kind())
   364  	}
   365  
   366  	var keyBuf [128]byte
   367  	cellKey := append(append(keyBuf[:0], []byte(ctx.StateScope())...), []byte(subKey)...)
   368  
   369  	msgs := make([]*Msg, 0, len(schema.Attrs))
   370  	err := ctx.User().HomePlanet().ReadCell(cellKey, schema, func(msg *Msg) {
   371  		switch msg.Op {
   372  		case MsgOp_PushAttr:
   373  			msgs = append(msgs, msg)
   374  		}
   375  	})
   376  	if err != nil {
   377  		return err
   378  	}
   379  
   380  	numFields := dst.NumField()
   381  	valType := dst.Type()
   382  
   383  	for fi := 0; fi < numFields; fi++ {
   384  		field := valType.Field(fi)
   385  		for _, ai := range schema.Attrs {
   386  			if ai.TypedName == field.Name {
   387  				for _, msg := range msgs {
   388  					if msg.AttrID == ai.AttrID {
   389  						msg.LoadVal(dst.Field(fi).Addr().Interface())
   390  						goto nextField
   391  					}
   392  				}
   393  			}
   394  		}
   395  	nextField:
   396  	}
   397  	return err
   398  }
   399  
   400  // WriteCell is the write analog of ReadCell.
   401  func WriteCell(ctx AppContext, subKey string, schema *AttrSchema, srcStruct any) error {
   402  
   403  	src := reflect.Indirect(reflect.ValueOf(srcStruct))
   404  	switch src.Kind() {
   405  	case reflect.Pointer:
   406  		src = src.Elem()
   407  	case reflect.Struct:
   408  	default:
   409  		return ErrCode_ExportErr.Errorf("expected struct, got %v", src.Kind())
   410  	}
   411  
   412  	{
   413  		tx := NewMsgBatch()
   414  		msg := tx.AddMsg()
   415  		msg.Op = MsgOp_UpsertCell
   416  		msg.ValType = ValType_SchemaID.Ord()
   417  		msg.ValInt = int64(schema.SchemaID)
   418  		msg.ValBuf = append(append(msg.ValBuf[:0], []byte(ctx.StateScope())...), []byte(subKey)...)
   419  
   420  		numFields := src.NumField()
   421  		valType := src.Type()
   422  
   423  		for _, attr := range schema.Attrs {
   424  			msg := tx.AddMsg()
   425  			msg.Op = MsgOp_PushAttr
   426  			msg.AttrID = attr.AttrID
   427  			for i := 0; i < numFields; i++ {
   428  				if valType.Field(i).Name == attr.TypedName {
   429  					msg.setVal(src.Field(i).Interface())
   430  					break
   431  				}
   432  			}
   433  			if msg.ValType == ValType_nil.Ord() {
   434  				panic("missing field")
   435  			}
   436  		}
   437  
   438  		msg = tx.AddMsg()
   439  		msg.Op = MsgOp_Commit
   440  
   441  		if err := ctx.User().HomePlanet().PushTx(tx); err != nil {
   442  			return err
   443  		}
   444  	}
   445  
   446  	return nil
   447  }
   448  
   449  
   450  func (req *CellReq) GetKwArg(argKey string) (string, bool) {
   451  	for _, arg := range req.Args {
   452  		if arg.Key == argKey {
   453  			if arg.Val != "" {
   454  				return arg.Val, true
   455  			}
   456  			return string(arg.ValBuf), true
   457  		}
   458  	}
   459  	return "", false
   460  }
   461  
   462  func (req *CellReq) GetChildSchema(modelURI string) *AttrSchema {
   463  	for _, schema := range req.ChildSchemas {
   464  		if schema.CellDataModel == modelURI {
   465  			return schema
   466  		}
   467  	}
   468  	return nil
   469  }
   470  
   471  func (req *CellReq) PushBeginPin(target CellID) {
   472  	m := NewMsg()
   473  	m.CellID = target.U64()
   474  	m.Op = MsgOp_PinCell
   475  	req.PushUpdate(m)
   476  }
   477  
   478  func (req *CellReq) PushInsertCell(target CellID, schema *AttrSchema) {
   479  	if schema != nil {
   480  		m := NewMsg()
   481  		m.CellID = target.U64()
   482  		m.Op = MsgOp_InsertChildCell
   483  		m.ValType = int32(ValType_SchemaID)
   484  		m.ValInt = int64(schema.SchemaID)
   485  		req.PushUpdate(m)
   486  	}
   487  }
   488  
   489  // Pushes the given attr to the client
   490  func (req *CellReq) PushAttr(target CellID, schema *AttrSchema, attrURI string, val Value) {
   491  	attr := schema.LookupAttr(attrURI)
   492  	if attr == nil {
   493  		return
   494  	}
   495  
   496  	m := NewMsg()
   497  	m.CellID = target.U64()
   498  	m.Op = MsgOp_PushAttr
   499  	m.AttrID = attr.AttrID
   500  	if attr.SeriesType == SeriesType_Fixed {
   501  		m.SI = attr.BoundSI
   502  	}
   503  	val.MarshalToMsg(m)
   504  	if attr.ValTypeID != 0 { // what is this for!?
   505  		m.ValType = int32(attr.ValTypeID)
   506  	}
   507  	req.PushUpdate(m)
   508  }
   509  
   510  func (req *CellReq) PushCheckpoint(err error) {
   511  	m := NewMsg()
   512  	m.Op = MsgOp_Commit
   513  	m.CellID = req.PinCell.U64()
   514  	if err != nil {
   515  		m.setVal(err)
   516  	}
   517  	req.PushUpdate(m)
   518  }
   519  
   520  */