Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[KATC] Prepare ee/indexeddb for use in KATC tables #1767

Merged
merged 1 commit into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 6 additions & 27 deletions ee/indexeddb/indexeddb.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var indexeddbComparer = newChromeComparer()

// QueryIndexeddbObjectStore queries the indexeddb at the given location `dbLocation`,
// returning all objects in the given database that live in the given object store.
func QueryIndexeddbObjectStore(dbLocation string, dbName string, objectStoreName string) ([]map[string]any, error) {
func QueryIndexeddbObjectStore(dbLocation string, dbName string, objectStoreName string) ([]map[string][]byte, error) {
// If Chrome is open, we won't be able to open the db. So, copy it to a temporary location first.
tempDbCopyLocation, err := copyIndexeddb(dbLocation)
if err != nil {
Expand Down Expand Up @@ -91,42 +91,21 @@ func QueryIndexeddbObjectStore(dbLocation string, dbName string, objectStoreName
return nil, errors.New("unable to get object store ID")
}

// Get the key path for all objects in this store
keyPathRaw, err := db.Get(objectStoreKeyPathKey(databaseId, objectStoreId), nil)
if err != nil {
return nil, fmt.Errorf("getting key path: %w", err)
}
keyPath, err := decodeIDBKeyPath(keyPathRaw)
if err != nil {
return nil, fmt.Errorf("decoding key path: %w", err)
}

// Get the key prefix for all objects in this store.
keyPrefix := objectDataKeyPrefix(databaseId, objectStoreId)

// Now, we can read all records, parsing only the ones with our matching key prefix.
objs := make([]map[string]any, 0)
// Now, we can read all records, keeping only the ones with our matching key prefix.
objs := make([]map[string][]byte, 0)
iter := db.NewIterator(nil, nil)
for iter.Next() {
key := iter.Key()
if !bytes.HasPrefix(key, keyPrefix) {
continue
}

keyVal, err := decodeIDBKey(key, keyPrefix)
if err != nil {
return objs, fmt.Errorf("decoding key: %w", err)
}

obj, err := deserializeIndexeddbValue(iter.Value())
if err != nil {
return objs, fmt.Errorf("decoding object: %w", err)
}

// Set the key path in the object -- add idb_ prefix to avoid collisions
obj[fmt.Sprintf("idb_%s", string(keyPath))] = keyVal

objs = append(objs, obj)
objs = append(objs, map[string][]byte{
"data": iter.Value(),
})
}
iter.Release()
if err := iter.Error(); err != nil {
Expand Down
62 changes: 0 additions & 62 deletions ee/indexeddb/keys.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package indexeddb

import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"path/filepath"
"strings"
Expand All @@ -22,9 +20,6 @@ const (
// Index IDs
objectStoreDataIndexId = 0x01 // 1

// Key types
keyTypeNumber = 0x03 // 3

// When parsing the origin from the database location, I have to add @1 at the end for the origin to be complete.
// I don't know why.
originSuffix = "@1"
Expand Down Expand Up @@ -77,24 +72,6 @@ func objectStoreNameKey(dbId uint64, objectStoreId uint64) []byte {
return append(storeNameKey, 0x00)
}

// objectStoreKeyPathKey constructs a query for the key path for the object store with the given ID.
func objectStoreKeyPathKey(dbId uint64, objectStoreId uint64) []byte {
// Key takes the format <0, database id, 0, 0, 50, object store id, 1>.
storeNameKey := []byte{0x00}
storeNameKey = append(storeNameKey, uvarintToBytes(dbId)...)
storeNameKey = append(storeNameKey,
0x00,
0x00,
objectStoreMetaDataTypeByte,
)

// Add the object store ID
storeNameKey = append(storeNameKey, uvarintToBytes(objectStoreId)...)

// Add 0x01, indicating we're querying for the object store name
return append(storeNameKey, 0x01)
}

// objectDataKeyPrefix returns the key prefix shared by all objects stored in the given database
// and in the given store.
func objectDataKeyPrefix(dbId uint64, objectStoreId uint64) []byte {
Expand All @@ -110,45 +87,6 @@ func decodeUtf16BigEndianBytes(b []byte) ([]byte, error) {
return utf16BigEndianDecoder.Bytes(b)
}

// decodeIDBKeyPath extracts the key path from the given input. IDBKeyPaths have multiple types.
// This function only supports string types, which take the format 0x00, 0x00, 0x01, StringWithLength.
func decodeIDBKeyPath(b []byte) ([]byte, error) {
if !bytes.HasPrefix(b, []byte{0x00, 0x00, 0x01}) {
return nil, errors.New("unsupported IDBKeyPath type")
}

if len(b) < 4 {
return nil, fmt.Errorf("IDBKeyPath with length %d is too short to be a string", len(b))
}

// Read the StringWithLength's length, but discard it -- we can just decode the remainder
// of the slice.
prefixLen := 3
_, bytesRead := binary.Uvarint(b[prefixLen:])

return decodeUtf16BigEndianBytes(b[prefixLen+bytesRead:])
}

// decodeIDBKey extracts the object key from the given data. It currently only supports
// numerical keys.
func decodeIDBKey(key []byte, keyPrefix []byte) (any, error) {
key = bytes.TrimPrefix(key, keyPrefix)

// Next byte is key type.
switch key[0] {
case keyTypeNumber:
// IEEE754 64-bit (double), in host endianness
buf := bytes.NewReader(key[1:])
var keyData float64
if err := binary.Read(buf, binary.NativeEndian, &keyData); err != nil {
return nil, fmt.Errorf("reading double: %w", err)
}
return keyData, nil
default:
return nil, fmt.Errorf("unimplemented key type 0x%02x", key[0])
}
}

// stringWithLength constructs an appropriate representation of `s`.
// See: https://github.com/chromium/chromium/blob/main/content/browser/indexed_db/docs/leveldb_coding_scheme.md#types
func stringWithLength(s string) ([]byte, error) {
Expand Down
57 changes: 0 additions & 57 deletions ee/indexeddb/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,6 @@ func Test_objectStoreNameKey(t *testing.T) {
require.Equal(t, expectedKey, objectStoreNameKey(dbId, objectStoreId), "object store name key format is incorrect")
}

func Test_objectStoreKeyPathKey(t *testing.T) {
t.Parallel()

var dbId uint64 = 2
var objectStoreId uint64 = 3

// Key takes the format <0, database id, 0, 0, 50, object store id, 1>.
expectedKey := []byte{
0x00,
0x02, // DB ID
0x00,
0x00,
objectStoreMetaDataTypeByte,
0x03, // object store ID
0x01,
}

require.Equal(t, expectedKey, objectStoreKeyPathKey(dbId, objectStoreId), "object store key path key format is incorrect")
}

func Test_objectDataKeyPrefix(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -102,43 +82,6 @@ func Test_decodeUtf16BigEndianBytes(t *testing.T) {
require.Equal(t, originalBytes, actualBytes, "decoded bytes do not match")
}

func Test_decodeIDBKeyPath(t *testing.T) {
t.Parallel()

// Prepare key path
keyPath := []byte("id")
utf16BigEndianEncoder := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewEncoder()
utf16KeyPathBytes, err := utf16BigEndianEncoder.Bytes(keyPath)
require.NoError(t, err, "encoding bytes")

testKeyPath := []byte{
0x00, 0x00, 0x01, // prefix
0x02, // length of "id"
}
testKeyPath = append(testKeyPath, utf16KeyPathBytes...)

resultKeyPath, err := decodeIDBKeyPath(testKeyPath)
require.NoError(t, err, "decoding key path")
require.Equal(t, keyPath, resultKeyPath)
}

func Test_decodeIDBKey(t *testing.T) {
t.Parallel()

// Prepare key value
var keyValue float64 = 4
keyValueBuf := bytes.NewBuffer(make([]byte, 0))
require.NoError(t, binary.Write(keyValueBuf, binary.NativeEndian, keyValue), "writing key value")

testKeyPrefix := []byte{0x00, 0x01, 0x01, 0x01}
testKey := append(testKeyPrefix, keyTypeNumber)
testKey = append(testKey, keyValueBuf.Bytes()...)

actualVal, err := decodeIDBKey(testKey, testKeyPrefix)
require.NoError(t, err, "decoding idb key")
require.Equal(t, keyValue, actualVal)
}

func Test_stringWithLength(t *testing.T) {
t.Parallel()

Expand Down
Loading
Loading