// Copyright 2022 Molecula Corp. (DBA FeatureBase). // SPDX-License-Identifier: Apache-2.0 // Package txkey consolidates in one place the use of keys to index into our // various storage/txn back-ends. The short_txkey version omits the // index and shard, since these are implicitly part of our database-per-shard // in an index scheme. In other words, every database is only in exactly // one shard of one index already. There is no need to repeat the index // and shard in these keys. package short_txkey import ( "encoding/binary" "fmt" ) // FieldView is here to avoid circular import. type FieldView struct { Field string View string } func FieldViewFromPrefix(prefix []byte) FieldView { field, view := SplitPrefix(prefix) return FieldView{Field: field, View: view} } func FieldViewFromFullKey(fullKey []byte) FieldView { field, view, _ := Split(fullKey) return FieldView{Field: field, View: view} } // Key produces the bytes that we use as a key to query the storage/tx engine. // The roaringContainerKey argument to Key() is a container key into a roaring Container. // The return value from Key() is constructed as follows: // // ~field;view'. Keys always end with '#'. // Keys always contain exactly one each of ';' and '<', in that order. // The field is between the '~' and the ';'. It must be at least 1 byte long. // The view is between the ';' and the '<'. It must be at least 1 byte long. // The ckey is the 8 bytes between the '<' and the '#'. // The Prefix of a key ends at, and includes, the '<'. It is at least 13 bytes long. // The index, field, and view are not allowed to contain these reserved bytes: // // {'~', '>', ';', ':', '<', '#', '$', '%', '^', '(', ')', '*', '!'} // // The bytes {'+', '/', '-', '_', '.', and '=' can be used in index, field, and view; to enable // base-64 encoding. // // The shortest possible key is 14 bytes. It would be laid out like this: // // ~f;v<12345678# // 12345678901234 // // keys starting with '~' are regular value keys. // keys starting with '>' are symlink keys. // // NB must be kept in sync with Prefix() and KeyExtractContainerKey(). func Key(index, field, view string, shard, roaringContainerKey uint64) (r []byte) { prefix := Prefix(index, field, view, shard) var ckey [9]byte binary.BigEndian.PutUint64(ckey[:8], roaringContainerKey) ckey[8] = byte('#') return append(prefix, ckey[:]...) } // KeyAndPrefix returns the equivalent of Key() and Prefix() calls. func KeyAndPrefix(index, field, view string, shard, roaringContainerKey uint64) (key, prefix []byte) { prefix = Prefix(index, field, view, shard) var ckey [9]byte binary.BigEndian.PutUint64(ckey[:8], roaringContainerKey) ckey[8] = byte('#') key = append(prefix, ckey[:]...) return } var _ = KeyAndPrefix // keep linter happy func MustValidateKey(bkey []byte) { n := len(bkey) if n < 14 { panic(fmt.Sprintf("bkey too short, must have at least 14 bytes: '%v'", string(bkey))) } typ := bkey[0] if typ != '~' && typ != '>' { panic(fmt.Sprintf("bkey did not start with '~' for value nor '>' for symlink: '%v'", string(bkey))) } if bkey[n-10] != '<' { panic(fmt.Sprintf("bkey did not have '<' at 9 bytes from the end: '%v'", string(bkey))) } if bkey[n-1] != '#' { panic(fmt.Sprintf("bkey did not end in '#': '%v'", string(bkey))) } } // KeyExtractContainerKey extracts the containerKey from bkey. // key example: field;view