// Copyright 2020 Pilosa Corp. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package txkey consolidates in one place the use of keys to index into our // various storage/txn back-ends. Databases LMDB and rbfDB both use it, // so that debug Dumps are comparable. package 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: // // ~index%field;view:shard'. Keys always end with '#'. // Keys always contain exactly one each of '%', ';', ':' and '<', in that order. // The index is between the first byte and the '%'. It must be at least 1 byte long. // 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 shard is the 8 bytes between the ':' and the '<'. // The ckey is the 8 bytes between the '<' and the '#'. // The Prefix of a key ends at, and includes, the '<'. It is at least 16 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 25 bytes. It would be laid out like this: // ~i%f;v:12345678<12345678# // 1234567890123456789012345 // // 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 uint64, 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[:]...) } // ShardFromKey key example: index/field;view:shard' 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-19] != ':' { panic(fmt.Sprintf("bkey did not have '<' at 18 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: index/field;view:shard