dfindexeddb 20241105__py3-none-any.whl → 20260205__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. dfindexeddb/indexeddb/chromium/blink.py +116 -74
  2. dfindexeddb/indexeddb/chromium/definitions.py +240 -125
  3. dfindexeddb/indexeddb/chromium/record.py +651 -346
  4. dfindexeddb/indexeddb/chromium/sqlite.py +362 -0
  5. dfindexeddb/indexeddb/chromium/v8.py +100 -78
  6. dfindexeddb/indexeddb/cli.py +282 -121
  7. dfindexeddb/indexeddb/firefox/definitions.py +7 -4
  8. dfindexeddb/indexeddb/firefox/gecko.py +98 -74
  9. dfindexeddb/indexeddb/firefox/record.py +78 -26
  10. dfindexeddb/indexeddb/safari/definitions.py +5 -3
  11. dfindexeddb/indexeddb/safari/record.py +86 -53
  12. dfindexeddb/indexeddb/safari/webkit.py +85 -71
  13. dfindexeddb/indexeddb/types.py +4 -1
  14. dfindexeddb/leveldb/cli.py +146 -138
  15. dfindexeddb/leveldb/definitions.py +6 -2
  16. dfindexeddb/leveldb/descriptor.py +70 -56
  17. dfindexeddb/leveldb/ldb.py +39 -33
  18. dfindexeddb/leveldb/log.py +41 -30
  19. dfindexeddb/leveldb/plugins/chrome_notifications.py +30 -18
  20. dfindexeddb/leveldb/plugins/interface.py +5 -6
  21. dfindexeddb/leveldb/plugins/manager.py +10 -9
  22. dfindexeddb/leveldb/record.py +71 -62
  23. dfindexeddb/leveldb/utils.py +105 -13
  24. dfindexeddb/utils.py +36 -31
  25. dfindexeddb/version.py +2 -2
  26. dfindexeddb-20260205.dist-info/METADATA +171 -0
  27. dfindexeddb-20260205.dist-info/RECORD +41 -0
  28. {dfindexeddb-20241105.dist-info → dfindexeddb-20260205.dist-info}/WHEEL +1 -1
  29. dfindexeddb-20241105.dist-info/AUTHORS +0 -12
  30. dfindexeddb-20241105.dist-info/METADATA +0 -424
  31. dfindexeddb-20241105.dist-info/RECORD +0 -41
  32. {dfindexeddb-20241105.dist-info → dfindexeddb-20260205.dist-info}/entry_points.txt +0 -0
  33. {dfindexeddb-20241105.dist-info → dfindexeddb-20260205.dist-info/licenses}/LICENSE +0 -0
  34. {dfindexeddb-20241105.dist-info → dfindexeddb-20260205.dist-info}/top_level.txt +0 -0
@@ -15,28 +15,32 @@
15
15
  """Parses Chromium IndexedDb structures."""
16
16
  from __future__ import annotations
17
17
 
18
- from dataclasses import dataclass, field
19
- from datetime import datetime
20
18
  import io
21
19
  import pathlib
22
20
  import sys
23
21
  import traceback
24
- from typing import Any, BinaryIO, Generator, Optional, Tuple, Type, TypeVar, \
25
- Union
26
-
27
- import snappy
22
+ from dataclasses import dataclass, field
23
+ from datetime import datetime
24
+ from typing import (
25
+ Any,
26
+ BinaryIO,
27
+ ClassVar,
28
+ Generator,
29
+ Optional,
30
+ Tuple,
31
+ Type,
32
+ TypeVar,
33
+ Union,
34
+ )
28
35
 
29
36
  from dfindexeddb import errors
30
- from dfindexeddb.indexeddb.chromium import blink
31
- from dfindexeddb.indexeddb.chromium import definitions
32
- from dfindexeddb.leveldb import record
33
- from dfindexeddb.leveldb import utils
34
-
37
+ from dfindexeddb.indexeddb.chromium import blink, definitions
38
+ from dfindexeddb.leveldb import record, utils
35
39
 
36
- T = TypeVar('T')
40
+ T = TypeVar("T", bound="BaseIndexedDBKey")
37
41
 
38
42
 
39
- @dataclass
43
+ @dataclass(frozen=True)
40
44
  class KeyPrefix(utils.FromDecoderMixin):
41
45
  """The IndexedDB key prefix.
42
46
 
@@ -46,6 +50,7 @@ class KeyPrefix(utils.FromDecoderMixin):
46
50
  object_store_id: the object store ID.
47
51
  index_id: the index ID.
48
52
  """
53
+
49
54
  offset: int = field(compare=False)
50
55
  database_id: int
51
56
  object_store_id: int
@@ -53,9 +58,7 @@ class KeyPrefix(utils.FromDecoderMixin):
53
58
 
54
59
  @classmethod
55
60
  def FromDecoder(
56
- cls,
57
- decoder: utils.LevelDBDecoder,
58
- base_offset: int = 0
61
+ cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
59
62
  ) -> KeyPrefix:
60
63
  """Decodes a KeyPrefix from the current position of a LevelDBDecoder.
61
64
 
@@ -72,28 +75,27 @@ class KeyPrefix(utils.FromDecoderMixin):
72
75
  """
73
76
  offset, raw_prefix = decoder.ReadBytes(1)
74
77
 
75
- database_id_length = (raw_prefix[0] & 0xE0 >> 5) + 1
76
- object_store_id_length = (raw_prefix[0] & 0x1C >> 2) + 1
78
+ database_id_length = ((raw_prefix[0] & 0xE0) >> 5) + 1
79
+ object_store_id_length = ((raw_prefix[0] & 0x1C) >> 2) + 1
77
80
  index_id_length = (raw_prefix[0] & 0x03) + 1
78
-
79
81
  if database_id_length < 1 or database_id_length > 8:
80
- raise errors.ParserError('Invalid database ID length')
82
+ raise errors.ParserError("Invalid database ID length")
81
83
 
82
84
  if object_store_id_length < 1 or object_store_id_length > 8:
83
- raise errors.ParserError('Invalid object store ID length')
85
+ raise errors.ParserError("Invalid object store ID length")
84
86
 
85
87
  if index_id_length < 1 or index_id_length > 4:
86
- raise errors.ParserError('Invalid index ID length')
87
-
88
- _, database_id = decoder.DecodeInt(database_id_length)
89
- _, object_store_id = decoder.DecodeInt(object_store_id_length)
90
- _, index_id = decoder.DecodeInt(index_id_length)
88
+ raise errors.ParserError("Invalid index ID length")
91
89
 
90
+ _, database_id = decoder.DecodeInt(database_id_length, signed=False)
91
+ _, object_store_id = decoder.DecodeInt(object_store_id_length, signed=False)
92
+ _, index_id = decoder.DecodeInt(index_id_length, signed=False)
92
93
  return cls(
93
94
  offset=base_offset + offset,
94
95
  database_id=database_id,
95
96
  object_store_id=object_store_id,
96
- index_id=index_id)
97
+ index_id=index_id,
98
+ )
97
99
 
98
100
  def GetKeyPrefixType(self) -> definitions.KeyPrefixType:
99
101
  """Returns the KeyPrefixType.
@@ -116,7 +118,8 @@ class KeyPrefix(utils.FromDecoderMixin):
116
118
  if self.index_id >= 30:
117
119
  return definitions.KeyPrefixType.INDEX_DATA
118
120
  raise errors.ParserError(
119
- f'Unknown KeyPrefixType (index_id={self.index_id})')
121
+ f"Unknown KeyPrefixType (index_id={self.index_id})"
122
+ )
120
123
 
121
124
 
122
125
  @dataclass
@@ -128,9 +131,10 @@ class IDBKey(utils.FromDecoderMixin):
128
131
  type: the type of the IDBKey.
129
132
  value: the value of the IDBKey.
130
133
  """
134
+
131
135
  offset: int = field(compare=False)
132
136
  type: definitions.IDBKeyType
133
- value: Union[list, bytes, str, float, datetime, None]
137
+ value: Union[list[Any], bytes, str, float, datetime, None]
134
138
 
135
139
  _MAXIMUM_DEPTH = 2000
136
140
 
@@ -138,7 +142,7 @@ class IDBKey(utils.FromDecoderMixin):
138
142
  def FromDecoder(
139
143
  cls,
140
144
  decoder: utils.LevelDBDecoder,
141
- base_offset: int = 0 #pylint: disable=unused-argument
145
+ base_offset: int = 0, # pylint: disable=unused-argument
142
146
  ) -> IDBKey:
143
147
  """Decodes an IDBKey from the current position of a LevelDBDecoder.
144
148
 
@@ -155,10 +159,13 @@ class IDBKey(utils.FromDecoderMixin):
155
159
  RecursionError: if maximum depth encountered during parsing.
156
160
  """
157
161
 
158
- def RecursiveParse(depth: int) -> Tuple[
159
- int, definitions.IDBKeyType, Union[
160
- list, bytes, str, float, datetime, None
161
- ]]:
162
+ def RecursiveParse(
163
+ depth: int,
164
+ ) -> Tuple[
165
+ int,
166
+ definitions.IDBKeyType,
167
+ Union[list[Any], bytes, str, float, datetime, None],
168
+ ]:
162
169
  """Recursively parses IDBKeys.
163
170
 
164
171
  Args:
@@ -174,16 +181,17 @@ class IDBKey(utils.FromDecoderMixin):
174
181
  RecursionError: if maximum depth encountered during parsing.
175
182
  """
176
183
  if depth == cls._MAXIMUM_DEPTH:
177
- raise RecursionError('Maximum recursion depth encountered during parse')
184
+ raise RecursionError("Maximum recursion depth encountered during parse")
178
185
  offset, key_type_value = decoder.DecodeInt(1)
179
186
  key_type = definitions.IDBKeyType(key_type_value)
187
+ value: Any = None
180
188
 
181
189
  if key_type == definitions.IDBKeyType.NULL:
182
190
  value = None
183
191
  elif key_type == definitions.IDBKeyType.ARRAY:
184
192
  _, length = decoder.DecodeVarint()
185
193
  if length < 0:
186
- raise errors.ParserError('Invalid length encountered')
194
+ raise errors.ParserError("Invalid length encountered")
187
195
  value = []
188
196
  while length:
189
197
  entry = RecursiveParse(depth + 1)
@@ -195,19 +203,108 @@ class IDBKey(utils.FromDecoderMixin):
195
203
  _, value = decoder.DecodeStringWithLength()
196
204
  elif key_type == definitions.IDBKeyType.DATE:
197
205
  _, raw_value = decoder.DecodeDouble()
198
- value = datetime.utcfromtimestamp(raw_value/1000.0)
206
+ value = datetime.utcfromtimestamp(raw_value / 1000.0)
199
207
  elif key_type == definitions.IDBKeyType.NUMBER:
200
208
  _, value = decoder.DecodeDouble()
201
209
  elif key_type == definitions.IDBKeyType.MIN_KEY:
202
210
  value = None
203
211
  else:
204
- raise errors.ParserError('Invalid IndexedDbKeyType')
212
+ raise errors.ParserError("Invalid IndexedDbKeyType")
205
213
  return offset, key_type, value
206
214
 
207
215
  offset, key_type, value = RecursiveParse(0)
208
216
  return cls(base_offset + offset, key_type, value)
209
217
 
210
218
 
219
+ @dataclass(frozen=True)
220
+ class SortableIDBKey(utils.FromDecoderMixin):
221
+ """A sortable IDBKey.
222
+
223
+ Attributes:
224
+ offset: the offset of the IDBKey.
225
+ type: the type of the IDBKey.
226
+ value: the value of the IDBKey.
227
+ """
228
+
229
+ offset: int = field(compare=False)
230
+ type: definitions.IDBKeyType
231
+ value: Union[list[Any], bytes, str, float, datetime, None]
232
+
233
+ _MAXIMUM_DEPTH = 2000
234
+
235
+ @classmethod
236
+ def FromDecoder(
237
+ cls,
238
+ decoder: utils.LevelDBDecoder,
239
+ base_offset: int = 0,
240
+ ) -> SortableIDBKey:
241
+ """Decodes a sortable IDBKey from the current position of a LevelDBDecoder.
242
+
243
+ Args:
244
+ decoder: the LevelDBDecoder.
245
+ base_offset: the base offset.
246
+
247
+ Returns:
248
+ The decoded SortableIDBKey.
249
+
250
+ Raises:
251
+ ParserError: on invalid key type or truncated data.
252
+ RecursionError: if maximum depth encountered.
253
+ """
254
+
255
+ def RecursiveParse(depth: int) -> Tuple[int, definitions.IDBKeyType, Any]:
256
+ """Recursively parses sortable IDBKeys.
257
+
258
+ Args:
259
+ depth: the current recursion depth.
260
+
261
+ Returns:
262
+ A tuple of the offset, the key type and the key value (where the value
263
+ can be bytes, str, float, datetime or a list of these types).
264
+
265
+ Raises:
266
+ ParserError: on invalid IDBKeyType or invalid array length during
267
+ parsing.
268
+ RecursionError: if maximum depth encountered during parsing.
269
+ """
270
+ if depth == cls._MAXIMUM_DEPTH:
271
+ raise RecursionError("Maximum recursion depth encountered")
272
+
273
+ value: Any = None
274
+ offset, ordered_type = decoder.DecodeUint8()
275
+ if ordered_type == definitions.OrderedIDBKeyType.NUMBER:
276
+ _, value = decoder.DecodeSortableDouble()
277
+ return offset, definitions.IDBKeyType.NUMBER, value
278
+ if ordered_type == definitions.OrderedIDBKeyType.DATE:
279
+ _, raw_date = decoder.DecodeSortableDouble()
280
+ return (
281
+ offset,
282
+ definitions.IDBKeyType.DATE,
283
+ datetime.utcfromtimestamp(raw_date / 1000.0),
284
+ )
285
+ if ordered_type == definitions.OrderedIDBKeyType.STRING:
286
+ _, value = decoder.DecodeSortableString()
287
+ return offset, definitions.IDBKeyType.STRING, value
288
+ if ordered_type == definitions.OrderedIDBKeyType.BINARY:
289
+ _, value = decoder.DecodeSortableBinary()
290
+ return offset, definitions.IDBKeyType.BINARY, value
291
+ if ordered_type == definitions.OrderedIDBKeyType.ARRAY:
292
+ value = []
293
+ while True:
294
+ _, next_byte = decoder.PeekBytes(1)
295
+ if next_byte[0] == definitions.SENTINEL:
296
+ decoder.ReadBytes(1)
297
+ break
298
+ _, _, item = RecursiveParse(depth + 1)
299
+ value.append(item)
300
+ return offset, definitions.IDBKeyType.ARRAY, value
301
+
302
+ raise errors.ParserError(f"Unknown ordered key type {ordered_type}")
303
+
304
+ offset, key_type, value = RecursiveParse(0)
305
+ return cls(base_offset + offset, key_type, value)
306
+
307
+
211
308
  @dataclass
212
309
  class IDBKeyPath(utils.FromDecoderMixin):
213
310
  """An IDBKeyPath.
@@ -217,15 +314,14 @@ class IDBKeyPath(utils.FromDecoderMixin):
217
314
  type: the IDBKeyPath type.
218
315
  value: the IDBKeyPath value.
219
316
  """
317
+
220
318
  offset: int = field(compare=False)
221
319
  type: definitions.IDBKeyPathType
222
320
  value: Union[str, list[str], None]
223
321
 
224
322
  @classmethod
225
323
  def FromDecoder(
226
- cls,
227
- decoder: utils.LevelDBDecoder,
228
- base_offset: int = 0
324
+ cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
229
325
  ) -> IDBKeyPath:
230
326
  """Decodes an IDBKeyPath from the current position of a LevelDBDecoder.
231
327
 
@@ -240,11 +336,13 @@ class IDBKeyPath(utils.FromDecoderMixin):
240
336
  ParserError: on insufficient bytes or invalid array length during
241
337
  parsing or unsupported key path type.
242
338
  """
243
- buffer = decoder.stream.getvalue() #pytype: disable=attribute-error
339
+ buffer = decoder.stream.getvalue() # type: ignore[attr-defined]
244
340
  if len(buffer) < 3:
245
- raise errors.ParserError('Insufficient bytes to parse.')
341
+ raise errors.ParserError("Insufficient bytes to parse.")
342
+
343
+ value: str | list[str] | None = None
246
344
 
247
- if buffer[0:2] != b'\x00\x00':
345
+ if buffer[0:2] != b"\x00\x00":
248
346
  offset, value = decoder.DecodeString()
249
347
  return IDBKeyPath(offset, definitions.IDBKeyPathType.STRING, value)
250
348
 
@@ -261,12 +359,12 @@ class IDBKeyPath(utils.FromDecoderMixin):
261
359
  value = []
262
360
  offset, count = decoder.DecodeVarint()
263
361
  if count < 0:
264
- raise errors.ParserError(f'Invalid array length {count}')
362
+ raise errors.ParserError(f"Invalid array length {count}")
265
363
  for _ in range(count):
266
364
  _, entry = decoder.DecodeStringWithLength()
267
365
  value.append(entry)
268
366
  else:
269
- raise errors.ParserError(f'Unsupported key_path_type {key_path_type}.')
367
+ raise errors.ParserError(f"Unsupported key_path_type {key_path_type}.")
270
368
  return IDBKeyPath(base_offset + offset, key_path_type, value)
271
369
 
272
370
 
@@ -279,15 +377,14 @@ class BlobJournalEntry(utils.FromDecoderMixin):
279
377
  database_id (int): the database ID.
280
378
  blob_number (int): the blob number.
281
379
  """
380
+
282
381
  offset: int = field(compare=False)
283
382
  database_id: int
284
383
  blob_number: int
285
384
 
286
385
  @classmethod
287
386
  def FromDecoder(
288
- cls,
289
- decoder: utils.LevelDBDecoder,
290
- base_offset: int = 0
387
+ cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
291
388
  ) -> BlobJournalEntry:
292
389
  """Decodes a BlobJournalEntry from the current position of a LevelDBDecoder.
293
390
 
@@ -300,8 +397,11 @@ class BlobJournalEntry(utils.FromDecoderMixin):
300
397
  """
301
398
  offset, database_id = decoder.DecodeUint64Varint()
302
399
  _, blob_number = decoder.DecodeUint64Varint()
303
- return cls(offset=base_offset + offset, database_id=database_id,
304
- blob_number=blob_number)
400
+ return cls(
401
+ offset=base_offset + offset,
402
+ database_id=database_id,
403
+ blob_number=blob_number,
404
+ )
305
405
 
306
406
 
307
407
  @dataclass
@@ -312,14 +412,13 @@ class BlobJournal(utils.FromDecoderMixin):
312
412
  offset (int): the offset.
313
413
  entries (list[BlobJournalEntry]): the list of blob journal entries.
314
414
  """
415
+
315
416
  offset: int = field(compare=False)
316
417
  entries: list[BlobJournalEntry]
317
418
 
318
419
  @classmethod
319
420
  def FromDecoder(
320
- cls,
321
- decoder: utils.LevelDBDecoder,
322
- base_offset: int = 0
421
+ cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
323
422
  ) -> BlobJournal:
324
423
  """Decodes a BlobJournal from the current position of a LevelDBDecoder.
325
424
 
@@ -360,6 +459,7 @@ class BaseIndexedDBKey:
360
459
  offset: the offset of the key (after the key_prefix).
361
460
  key_prefix: the key prefix.
362
461
  """
462
+
363
463
  offset: int
364
464
  key_prefix: KeyPrefix
365
465
 
@@ -371,16 +471,19 @@ class BaseIndexedDBKey:
371
471
  Args:
372
472
  decoder: the stream decoder
373
473
 
474
+ Returns:
475
+ The decoded value.
476
+
374
477
  Raises:
375
478
  NotImplementedError.
376
479
  """
377
- raise NotImplementedError(f'{self.__class__.__name__}.decode_value')
480
+ raise NotImplementedError(f"{self.__class__.__name__}.decode_value")
378
481
 
379
482
  def ParseValue(self, value_data: bytes) -> Any:
380
483
  """Parses the value from raw bytes.
381
484
 
382
485
  Args:
383
- value_data: the raw value bytes.
486
+ value_data: the raw value data.
384
487
 
385
488
  Returns:
386
489
  The parsed value.
@@ -392,17 +495,19 @@ class BaseIndexedDBKey:
392
495
 
393
496
  @classmethod
394
497
  def FromDecoder(
395
- cls: T,
498
+ cls: Type[T],
396
499
  decoder: utils.LevelDBDecoder,
397
500
  key_prefix: KeyPrefix,
398
- base_offset: int = 0
399
- ) -> T: #pylint: disable=unused-variable
400
- """Decodes the remaining key data from the current decoder position.
501
+ base_offset: int = 0,
502
+ ) -> T:
503
+ """Parses the key from the current position of the LevelDBDecoder.
504
+
505
+ To be implemented by subclasses.
401
506
 
402
507
  Args:
403
508
  decoder: the stream decoder.
404
- key_prefix: the decoded key_prefix.
405
- base_offset: the base offset.
509
+ key_prefix: the key prefix.
510
+ base_offset: the base offset of the key.
406
511
 
407
512
  Returns:
408
513
  The decoded key.
@@ -410,7 +515,7 @@ class BaseIndexedDBKey:
410
515
  Raises:
411
516
  NotImplementedError.
412
517
  """
413
- raise NotImplementedError(f'{cls.__class__.__name__}.decode_key')
518
+ raise NotImplementedError(f"{cls.__class__.__name__}.decode_key")
414
519
 
415
520
  @classmethod
416
521
  def FromStream(cls: Type[T], stream: BinaryIO, base_offset: int = 0) -> T:
@@ -426,7 +531,8 @@ class BaseIndexedDBKey:
426
531
  decoder = utils.LevelDBDecoder(stream)
427
532
  key_prefix = KeyPrefix.FromDecoder(decoder, base_offset=base_offset)
428
533
  return cls.FromDecoder(
429
- decoder=decoder, key_prefix=key_prefix, base_offset=base_offset)
534
+ decoder=decoder, key_prefix=key_prefix, base_offset=base_offset
535
+ )
430
536
 
431
537
  @classmethod
432
538
  def FromBytes(cls: Type[T], raw_data: bytes, base_offset: int = 0) -> T:
@@ -456,12 +562,12 @@ class SchemaVersionKey(BaseIndexedDBKey):
456
562
  cls,
457
563
  decoder: utils.LevelDBDecoder,
458
564
  key_prefix: KeyPrefix,
459
- base_offset: int = 0
565
+ base_offset: int = 0,
460
566
  ) -> SchemaVersionKey:
461
567
  """Decodes the schema version key."""
462
568
  offset, key_type = decoder.DecodeUint8()
463
569
  if key_type != definitions.GlobalMetadataKeyType.SCHEMA_VERSION:
464
- raise errors.ParserError('Not a SchemaVersionKey')
570
+ raise errors.ParserError("Not a SchemaVersionKey")
465
571
  return cls(offset=base_offset + offset, key_prefix=key_prefix)
466
572
 
467
573
 
@@ -478,12 +584,12 @@ class MaxDatabaseIdKey(BaseIndexedDBKey):
478
584
  cls,
479
585
  decoder: utils.LevelDBDecoder,
480
586
  key_prefix: KeyPrefix,
481
- base_offset: int = 0
587
+ base_offset: int = 0,
482
588
  ) -> MaxDatabaseIdKey:
483
589
  """Decodes the maximum database key."""
484
590
  offset, key_type = decoder.DecodeUint8()
485
591
  if key_type != definitions.GlobalMetadataKeyType.MAX_DATABASE_ID:
486
- raise errors.ParserError('Not a MaxDatabaseIdKey')
592
+ raise errors.ParserError("Not a MaxDatabaseIdKey")
487
593
  return cls(offset=base_offset + offset, key_prefix=key_prefix)
488
594
 
489
595
 
@@ -500,12 +606,12 @@ class DataVersionKey(BaseIndexedDBKey):
500
606
  cls,
501
607
  decoder: utils.LevelDBDecoder,
502
608
  key_prefix: KeyPrefix,
503
- base_offset: int = 0
609
+ base_offset: int = 0,
504
610
  ) -> DataVersionKey:
505
611
  """Decodes the data version key."""
506
612
  offset, key_type = decoder.DecodeUint8()
507
613
  if key_type != definitions.GlobalMetadataKeyType.DATA_VERSION:
508
- raise errors.ParserError('Not a DataVersionKey')
614
+ raise errors.ParserError("Not a DataVersionKey")
509
615
  return cls(offset=base_offset + offset, key_prefix=key_prefix)
510
616
 
511
617
 
@@ -522,12 +628,12 @@ class RecoveryBlobJournalKey(BaseIndexedDBKey):
522
628
  cls,
523
629
  decoder: utils.LevelDBDecoder,
524
630
  key_prefix: KeyPrefix,
525
- base_offset: int = 0
631
+ base_offset: int = 0,
526
632
  ) -> RecoveryBlobJournalKey:
527
633
  """Decodes the recovery blob journal key."""
528
634
  offset, key_type = decoder.DecodeUint8()
529
635
  if key_type != definitions.GlobalMetadataKeyType.RECOVERY_BLOB_JOURNAL:
530
- raise errors.ParserError('Not a RecoveryBlobJournalKey')
636
+ raise errors.ParserError("Not a RecoveryBlobJournalKey")
531
637
  return cls(offset=base_offset + offset, key_prefix=key_prefix)
532
638
 
533
639
 
@@ -544,12 +650,12 @@ class ActiveBlobJournalKey(BaseIndexedDBKey):
544
650
  cls,
545
651
  decoder: utils.LevelDBDecoder,
546
652
  key_prefix: KeyPrefix,
547
- base_offset: int = 0
653
+ base_offset: int = 0,
548
654
  ) -> ActiveBlobJournalKey:
549
655
  """Decodes the active blob journal value."""
550
656
  offset, key_type = decoder.DecodeUint8()
551
657
  if key_type != definitions.GlobalMetadataKeyType.ACTIVE_BLOB_JOURNAL:
552
- raise errors.ParserError('Not a ActiveBlobJournalKey')
658
+ raise errors.ParserError("Not a ActiveBlobJournalKey")
553
659
  return cls(offset=base_offset + offset, key_prefix=key_prefix)
554
660
 
555
661
 
@@ -560,19 +666,19 @@ class EarliestSweepKey(BaseIndexedDBKey):
560
666
  def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
561
667
  """Decodes the earliest sweep value."""
562
668
  _, data = decoder.ReadBytes()
563
- return int.from_bytes(data, byteorder='little', signed=False)
669
+ return int.from_bytes(data, byteorder="little", signed=False)
564
670
 
565
671
  @classmethod
566
672
  def FromDecoder(
567
673
  cls,
568
674
  decoder: utils.LevelDBDecoder,
569
675
  key_prefix: KeyPrefix,
570
- base_offset: int = 0
676
+ base_offset: int = 0,
571
677
  ) -> EarliestSweepKey:
572
678
  """Decodes the earliest sweep value."""
573
679
  offset, key_type = decoder.DecodeUint8()
574
680
  if key_type != definitions.GlobalMetadataKeyType.EARLIEST_SWEEP:
575
- raise errors.ParserError('Not a EarliestSweepKey')
681
+ raise errors.ParserError("Not a EarliestSweepKey")
576
682
  return cls(offset=base_offset + offset, key_prefix=key_prefix)
577
683
 
578
684
 
@@ -583,19 +689,19 @@ class EarliestCompactionTimeKey(BaseIndexedDBKey):
583
689
  def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
584
690
  """Decodes the earliest compaction time value."""
585
691
  _, data = decoder.ReadBytes()
586
- return int.from_bytes(data, byteorder='little', signed=False)
692
+ return int.from_bytes(data, byteorder="little", signed=False)
587
693
 
588
694
  @classmethod
589
695
  def FromDecoder(
590
696
  cls,
591
697
  decoder: utils.LevelDBDecoder,
592
698
  key_prefix: KeyPrefix,
593
- base_offset: int = 0
699
+ base_offset: int = 0,
594
700
  ) -> EarliestCompactionTimeKey:
595
701
  """Decodes the earliest compaction time key."""
596
702
  offset, key_type = decoder.DecodeUint8()
597
703
  if key_type != definitions.GlobalMetadataKeyType.EARLIEST_COMPACTION_TIME:
598
- raise errors.ParserError('Not a EarliestCompactionTimeKey')
704
+ raise errors.ParserError("Not a EarliestCompactionTimeKey")
599
705
  return cls(offset=base_offset + offset, key_prefix=key_prefix)
600
706
 
601
707
 
@@ -605,7 +711,7 @@ class ScopesPrefixKey(BaseIndexedDBKey):
605
711
 
606
712
  def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Optional[bytes]:
607
713
  """Decodes the scopes prefix value."""
608
- if decoder.NumRemainingBytes:
714
+ if decoder.NumRemainingBytes():
609
715
  return decoder.ReadBytes()[1]
610
716
  return None
611
717
 
@@ -614,12 +720,12 @@ class ScopesPrefixKey(BaseIndexedDBKey):
614
720
  cls,
615
721
  decoder: utils.LevelDBDecoder,
616
722
  key_prefix: KeyPrefix,
617
- base_offset: int = 0
723
+ base_offset: int = 0,
618
724
  ) -> ScopesPrefixKey:
619
725
  """Decodes the scopes prefix key."""
620
726
  offset, key_type = decoder.DecodeUint8()
621
727
  if key_type != definitions.GlobalMetadataKeyType.SCOPES_PREFIX:
622
- raise errors.ParserError('Not a ScopesPrefixKey')
728
+ raise errors.ParserError("Not a ScopesPrefixKey")
623
729
  return cls(offset=base_offset + offset, key_prefix=key_prefix)
624
730
 
625
731
 
@@ -630,27 +736,30 @@ class DatabaseFreeListKey(BaseIndexedDBKey):
630
736
  Attributes:
631
737
  database_id: the database ID.
632
738
  """
739
+
633
740
  database_id: int
634
741
 
635
- def DecodeValue(self, decoder: utils.LevelDBDecoder):
742
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> None:
636
743
  """Decodes the database free list value."""
637
- raise NotImplementedError('DatabaseFreeListKey.decode_value')
744
+ raise NotImplementedError("DatabaseFreeListKey.decode_value")
638
745
 
639
746
  @classmethod
640
747
  def FromDecoder(
641
748
  cls,
642
749
  decoder: utils.LevelDBDecoder,
643
750
  key_prefix: KeyPrefix,
644
- base_offset: int = 0
751
+ base_offset: int = 0,
645
752
  ) -> DatabaseFreeListKey:
646
753
  """Decodes the database free list key."""
647
754
  offset, key_type = decoder.DecodeUint8()
648
755
  if key_type != definitions.GlobalMetadataKeyType.DATABASE_FREE_LIST:
649
- raise errors.ParserError('Not a DatabaseFreeListKey')
756
+ raise errors.ParserError("Not a DatabaseFreeListKey")
650
757
  _, database_id = decoder.DecodeVarint()
651
758
  return cls(
652
- offset=base_offset + offset, key_prefix=key_prefix,
653
- database_id=database_id)
759
+ offset=base_offset + offset,
760
+ key_prefix=key_prefix,
761
+ database_id=database_id,
762
+ )
654
763
 
655
764
 
656
765
  @dataclass
@@ -661,6 +770,7 @@ class DatabaseNameKey(BaseIndexedDBKey):
661
770
  origin: the origin of the database.
662
771
  database_name: the database name.
663
772
  """
773
+
664
774
  origin: str
665
775
  database_name: str
666
776
 
@@ -676,45 +786,44 @@ class DatabaseNameKey(BaseIndexedDBKey):
676
786
  cls,
677
787
  decoder: utils.LevelDBDecoder,
678
788
  key_prefix: KeyPrefix,
679
- base_offset: int = 0
789
+ base_offset: int = 0,
680
790
  ) -> DatabaseNameKey:
681
791
  """Decodes the database name key."""
682
792
  offset, key_type = decoder.DecodeUint8()
683
793
  if key_type != definitions.GlobalMetadataKeyType.DATABASE_NAME:
684
- raise errors.ParserError('Not a DatabaseNameKey')
794
+ raise errors.ParserError("Not a DatabaseNameKey")
685
795
  _, origin = decoder.DecodeStringWithLength()
686
796
  _, database_name = decoder.DecodeStringWithLength()
687
797
  return cls(
688
- offset=base_offset + offset, key_prefix=key_prefix, origin=origin,
689
- database_name=database_name)
798
+ offset=base_offset + offset,
799
+ key_prefix=key_prefix,
800
+ origin=origin,
801
+ database_name=database_name,
802
+ )
690
803
 
691
804
 
692
805
  @dataclass
693
806
  class GlobalMetaDataKey(BaseIndexedDBKey):
694
807
  """A GlobalMetaDataKey parser."""
695
808
 
696
- METADATA_TYPE_TO_CLASS = {
697
- definitions.GlobalMetadataKeyType
698
- .ACTIVE_BLOB_JOURNAL: ActiveBlobJournalKey,
699
- definitions.GlobalMetadataKeyType
700
- .DATA_VERSION: DataVersionKey,
701
- definitions.GlobalMetadataKeyType
702
- .DATABASE_FREE_LIST: DatabaseFreeListKey,
703
- definitions.GlobalMetadataKeyType
704
- .DATABASE_NAME: DatabaseNameKey,
705
- definitions.GlobalMetadataKeyType
706
- .EARLIEST_COMPACTION_TIME: EarliestCompactionTimeKey,
707
- definitions.GlobalMetadataKeyType
708
- .EARLIEST_SWEEP: EarliestSweepKey,
709
- definitions.GlobalMetadataKeyType
710
- .MAX_DATABASE_ID: MaxDatabaseIdKey,
711
- definitions.GlobalMetadataKeyType
712
- .RECOVERY_BLOB_JOURNAL: RecoveryBlobJournalKey,
713
- definitions.GlobalMetadataKeyType
714
- .SCHEMA_VERSION: SchemaVersionKey,
715
- definitions.GlobalMetadataKeyType
716
- .SCOPES_PREFIX: ScopesPrefixKey,
809
+ # pylint: disable=line-too-long
810
+ METADATA_TYPE_TO_CLASS: ClassVar[
811
+ dict[ # pylint: disable=invalid-name
812
+ definitions.GlobalMetadataKeyType, type[BaseIndexedDBKey]
813
+ ]
814
+ ] = {
815
+ definitions.GlobalMetadataKeyType.ACTIVE_BLOB_JOURNAL: ActiveBlobJournalKey,
816
+ definitions.GlobalMetadataKeyType.DATA_VERSION: DataVersionKey,
817
+ definitions.GlobalMetadataKeyType.DATABASE_FREE_LIST: DatabaseFreeListKey,
818
+ definitions.GlobalMetadataKeyType.DATABASE_NAME: DatabaseNameKey,
819
+ definitions.GlobalMetadataKeyType.EARLIEST_COMPACTION_TIME: EarliestCompactionTimeKey,
820
+ definitions.GlobalMetadataKeyType.EARLIEST_SWEEP: EarliestSweepKey,
821
+ definitions.GlobalMetadataKeyType.MAX_DATABASE_ID: MaxDatabaseIdKey,
822
+ definitions.GlobalMetadataKeyType.RECOVERY_BLOB_JOURNAL: RecoveryBlobJournalKey,
823
+ definitions.GlobalMetadataKeyType.SCHEMA_VERSION: SchemaVersionKey,
824
+ definitions.GlobalMetadataKeyType.SCOPES_PREFIX: ScopesPrefixKey,
717
825
  }
826
+ # pylint: enable=line-too-long
718
827
 
719
828
  def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Any:
720
829
  """Decodes the value from the current position of the LevelDBDecoder.
@@ -728,17 +837,8 @@ class GlobalMetaDataKey(BaseIndexedDBKey):
728
837
  cls,
729
838
  decoder: utils.LevelDBDecoder,
730
839
  key_prefix: KeyPrefix,
731
- base_offset: int = 0
732
- ) -> Union[ActiveBlobJournalKey,
733
- DataVersionKey,
734
- DatabaseFreeListKey,
735
- DatabaseNameKey,
736
- EarliestSweepKey,
737
- EarliestCompactionTimeKey,
738
- MaxDatabaseIdKey,
739
- RecoveryBlobJournalKey,
740
- SchemaVersionKey,
741
- ScopesPrefixKey]:
840
+ base_offset: int = 0,
841
+ ) -> BaseIndexedDBKey:
742
842
  """Decodes the global metadata key.
743
843
 
744
844
  Raises:
@@ -749,9 +849,8 @@ class GlobalMetaDataKey(BaseIndexedDBKey):
749
849
 
750
850
  key_class = cls.METADATA_TYPE_TO_CLASS.get(metadata_type)
751
851
  if not key_class:
752
- raise errors.ParserError('Unknown metadata key type')
753
- return key_class.FromDecoder(
754
- decoder, key_prefix, base_offset)
852
+ raise errors.ParserError("Unknown metadata key type")
853
+ return key_class.FromDecoder(decoder, key_prefix, base_offset)
755
854
 
756
855
 
757
856
  @dataclass
@@ -761,27 +860,30 @@ class ObjectStoreFreeListKey(BaseIndexedDBKey):
761
860
  Attributes:
762
861
  object_store_id: the ID of the object store containing the free list.
763
862
  """
863
+
764
864
  object_store_id: int
765
865
 
766
- def DecodeValue(self, decoder: utils.LevelDBDecoder):
866
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> None:
767
867
  """Decodes the object store free list value."""
768
- raise NotImplementedError('ObjectStoreFreeListKey.decode_value')
868
+ raise NotImplementedError("ObjectStoreFreeListKey.decode_value")
769
869
 
770
870
  @classmethod
771
871
  def FromDecoder(
772
872
  cls,
773
873
  decoder: utils.LevelDBDecoder,
774
874
  key_prefix: KeyPrefix,
775
- base_offset: int = 0
875
+ base_offset: int = 0,
776
876
  ) -> ObjectStoreFreeListKey:
777
877
  """Decodes the object store free list key."""
778
878
  offset, key_type = decoder.DecodeUint8()
779
879
  if key_type != definitions.DatabaseMetaDataKeyType.OBJECT_STORE_FREE_LIST:
780
- raise errors.ParserError('Not am ObjectStoreFreeListKey')
880
+ raise errors.ParserError("Not am ObjectStoreFreeListKey")
781
881
  _, object_store_id = decoder.DecodeVarint()
782
882
  return cls(
783
- offset=base_offset + offset, key_prefix=key_prefix,
784
- object_store_id=object_store_id)
883
+ offset=base_offset + offset,
884
+ key_prefix=key_prefix,
885
+ object_store_id=object_store_id,
886
+ )
785
887
 
786
888
 
787
889
  @dataclass
@@ -791,27 +893,30 @@ class IndexFreeListKey(BaseIndexedDBKey):
791
893
  Attributes:
792
894
  object_store_id: the ID of the object store containing the free list.
793
895
  """
896
+
794
897
  object_store_id: int
795
898
 
796
- def DecodeValue(self, decoder: utils.LevelDBDecoder):
899
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> None:
797
900
  """Decodes the index free list value."""
798
- raise NotImplementedError('IndexFreeListKey.decode_value')
901
+ raise NotImplementedError("IndexFreeListKey.decode_value")
799
902
 
800
903
  @classmethod
801
904
  def FromDecoder(
802
905
  cls,
803
906
  decoder: utils.LevelDBDecoder,
804
907
  key_prefix: KeyPrefix,
805
- base_offset: int = 0
806
- ) -> IndexFreeListKey:
908
+ base_offset: int = 0,
909
+ ) -> IndexFreeListKey:
807
910
  """Decodes the index free list key."""
808
911
  offset, key_type = decoder.DecodeUint8()
809
912
  if key_type != definitions.DatabaseMetaDataKeyType.INDEX_FREE_LIST:
810
- raise errors.ParserError('Not am IndexFreeListKey')
913
+ raise errors.ParserError("Not am IndexFreeListKey")
811
914
  _, object_store_id = decoder.DecodeVarint()
812
915
  return cls(
813
- offset=base_offset + offset, key_prefix=key_prefix,
814
- object_store_id=object_store_id)
916
+ offset=base_offset + offset,
917
+ key_prefix=key_prefix,
918
+ object_store_id=object_store_id,
919
+ )
815
920
 
816
921
 
817
922
  @dataclass
@@ -821,6 +926,7 @@ class ObjectStoreNamesKey(BaseIndexedDBKey):
821
926
  Attributes:
822
927
  object_store_name: the name of the object store.
823
928
  """
929
+
824
930
  object_store_name: str
825
931
 
826
932
  def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
@@ -829,17 +935,21 @@ class ObjectStoreNamesKey(BaseIndexedDBKey):
829
935
 
830
936
  @classmethod
831
937
  def FromDecoder(
832
- cls, decoder: utils.LevelDBDecoder,
938
+ cls,
939
+ decoder: utils.LevelDBDecoder,
833
940
  key_prefix: KeyPrefix,
834
- base_offset: int = 0
941
+ base_offset: int = 0,
835
942
  ) -> ObjectStoreNamesKey:
836
943
  """Decodes the object store names key."""
837
944
  offset, key_type = decoder.DecodeUint8()
838
945
  if key_type != definitions.DatabaseMetaDataKeyType.OBJECT_STORE_NAMES:
839
- raise errors.ParserError('Not am ObjectStoreNamesKey')
946
+ raise errors.ParserError("Not am ObjectStoreNamesKey")
840
947
  _, object_store_name = decoder.DecodeStringWithLength()
841
- return cls(key_prefix=key_prefix, offset=base_offset + offset,
842
- object_store_name=object_store_name)
948
+ return cls(
949
+ key_prefix=key_prefix,
950
+ offset=base_offset + offset,
951
+ object_store_name=object_store_name,
952
+ )
843
953
 
844
954
 
845
955
  @dataclass
@@ -849,6 +959,7 @@ class IndexNamesKey(BaseIndexedDBKey):
849
959
  Attributes:
850
960
  index_name: the name of the index.
851
961
  """
962
+
852
963
  index_name: str
853
964
 
854
965
  def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
@@ -860,15 +971,18 @@ class IndexNamesKey(BaseIndexedDBKey):
860
971
  cls,
861
972
  decoder: utils.LevelDBDecoder,
862
973
  key_prefix: KeyPrefix,
863
- base_offset: int = 0
974
+ base_offset: int = 0,
864
975
  ) -> IndexNamesKey:
865
976
  """Decodes the index names key."""
866
977
  offset, key_type = decoder.DecodeUint8()
867
978
  if key_type != definitions.DatabaseMetaDataKeyType.INDEX_NAMES:
868
- raise errors.ParserError('Not am IndexNamesKey')
979
+ raise errors.ParserError("Not am IndexNamesKey")
869
980
  _, index_name = decoder.DecodeStringWithLength()
870
- return cls(key_prefix=key_prefix, offset=base_offset + offset,
871
- index_name=index_name)
981
+ return cls(
982
+ key_prefix=key_prefix,
983
+ offset=base_offset + offset,
984
+ index_name=index_name,
985
+ )
872
986
 
873
987
 
874
988
  @dataclass
@@ -878,39 +992,45 @@ class DatabaseMetaDataKey(BaseIndexedDBKey):
878
992
  Attributes:
879
993
  metadata_type: the type of metadata that the key value contains.
880
994
  """
995
+
881
996
  metadata_type: definitions.DatabaseMetaDataKeyType
882
997
 
883
- def DecodeValue(
884
- self, decoder: utils.LevelDBDecoder) -> Union[str, int]:
998
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> Union[str, int]:
885
999
  """Decodes the database metadata value."""
886
- if (self.metadata_type ==
887
- definitions.DatabaseMetaDataKeyType.ORIGIN_NAME):
1000
+ if self.metadata_type == definitions.DatabaseMetaDataKeyType.ORIGIN_NAME:
888
1001
  return decoder.DecodeString()[1]
889
- if (self.metadata_type ==
890
- definitions.DatabaseMetaDataKeyType.DATABASE_NAME):
1002
+ if self.metadata_type == definitions.DatabaseMetaDataKeyType.DATABASE_NAME:
891
1003
  return decoder.DecodeString()[1]
892
- if (self.metadata_type ==
893
- definitions.DatabaseMetaDataKeyType.IDB_STRING_VERSION_DATA):
1004
+ if (
1005
+ self.metadata_type
1006
+ == definitions.DatabaseMetaDataKeyType.IDB_STRING_VERSION_DATA
1007
+ ):
894
1008
  return decoder.DecodeString()[1]
895
- if (self.metadata_type ==
896
- definitions.DatabaseMetaDataKeyType.MAX_ALLOCATED_OBJECT_STORE_ID):
1009
+ if (
1010
+ self.metadata_type
1011
+ == definitions.DatabaseMetaDataKeyType.MAX_ALLOCATED_OBJECT_STORE_ID
1012
+ ):
897
1013
  return decoder.DecodeInt()[1]
898
- if (self.metadata_type ==
899
- definitions.DatabaseMetaDataKeyType.IDB_INTEGER_VERSION):
1014
+ if (
1015
+ self.metadata_type
1016
+ == definitions.DatabaseMetaDataKeyType.IDB_INTEGER_VERSION
1017
+ ):
900
1018
  return decoder.DecodeVarint()[1]
901
- if (self.metadata_type ==
902
- definitions.DatabaseMetaDataKeyType
903
- .BLOB_NUMBER_GENERATOR_CURRENT_NUMBER):
1019
+ if (
1020
+ self.metadata_type
1021
+ == definitions.DatabaseMetaDataKeyType.BLOB_NUMBER_GENERATOR_CURRENT_NUMBER # pylint: disable=line-too-long
1022
+ ):
904
1023
  return decoder.DecodeVarint()[1]
905
1024
  raise errors.ParserError(
906
- f'Unknown database metadata type {self.metadata_type}')
1025
+ f"Unknown database metadata type {self.metadata_type}"
1026
+ )
907
1027
 
908
1028
  @classmethod
909
1029
  def FromDecoder(
910
1030
  cls,
911
1031
  decoder: utils.LevelDBDecoder,
912
1032
  key_prefix: KeyPrefix,
913
- base_offset: int = 0
1033
+ base_offset: int = 0,
914
1034
  ) -> Union[
915
1035
  DatabaseMetaDataKey,
916
1036
  IndexFreeListKey,
@@ -918,7 +1038,8 @@ class DatabaseMetaDataKey(BaseIndexedDBKey):
918
1038
  IndexNamesKey,
919
1039
  ObjectStoreFreeListKey,
920
1040
  ObjectStoreMetaDataKey,
921
- ObjectStoreNamesKey]:
1041
+ ObjectStoreNamesKey,
1042
+ ]:
922
1043
  """Decodes the database metadata key."""
923
1044
  offset, metadata_value = decoder.PeekBytes(1)
924
1045
  metadata_type = definitions.DatabaseMetaDataKeyType(metadata_value[0])
@@ -929,32 +1050,36 @@ class DatabaseMetaDataKey(BaseIndexedDBKey):
929
1050
  definitions.DatabaseMetaDataKeyType.IDB_STRING_VERSION_DATA,
930
1051
  definitions.DatabaseMetaDataKeyType.MAX_ALLOCATED_OBJECT_STORE_ID,
931
1052
  definitions.DatabaseMetaDataKeyType.IDB_INTEGER_VERSION,
932
- definitions.DatabaseMetaDataKeyType
933
- .BLOB_NUMBER_GENERATOR_CURRENT_NUMBER):
934
- return cls(key_prefix=key_prefix, offset=base_offset + offset,
935
- metadata_type=metadata_type)
1053
+ definitions.DatabaseMetaDataKeyType.BLOB_NUMBER_GENERATOR_CURRENT_NUMBER, # pylint: disable=line-too-long
1054
+ ):
1055
+ return cls(
1056
+ key_prefix=key_prefix,
1057
+ offset=base_offset + offset,
1058
+ metadata_type=metadata_type,
1059
+ )
936
1060
  if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_META_DATA:
937
- return IndexMetaDataKey.FromDecoder(
938
- decoder, key_prefix, base_offset)
939
- if (metadata_type ==
940
- definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA):
1061
+ return IndexMetaDataKey.FromDecoder(decoder, key_prefix, base_offset)
1062
+ if (
1063
+ metadata_type
1064
+ == definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA
1065
+ ):
941
1066
  return ObjectStoreMetaDataKey.FromDecoder(
942
- decoder, key_prefix, base_offset)
943
- if (metadata_type ==
944
- definitions.DatabaseMetaDataKeyType.OBJECT_STORE_FREE_LIST):
1067
+ decoder, key_prefix, base_offset
1068
+ )
1069
+ if (
1070
+ metadata_type
1071
+ == definitions.DatabaseMetaDataKeyType.OBJECT_STORE_FREE_LIST
1072
+ ):
945
1073
  return ObjectStoreFreeListKey.FromDecoder(
946
- decoder, key_prefix, base_offset)
1074
+ decoder, key_prefix, base_offset
1075
+ )
947
1076
  if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_FREE_LIST:
948
- return IndexFreeListKey.FromDecoder(
949
- decoder, key_prefix, base_offset)
950
- if (metadata_type ==
951
- definitions.DatabaseMetaDataKeyType.OBJECT_STORE_NAMES):
952
- return ObjectStoreNamesKey.FromDecoder(
953
- decoder, key_prefix, base_offset)
1077
+ return IndexFreeListKey.FromDecoder(decoder, key_prefix, base_offset)
1078
+ if metadata_type == definitions.DatabaseMetaDataKeyType.OBJECT_STORE_NAMES:
1079
+ return ObjectStoreNamesKey.FromDecoder(decoder, key_prefix, base_offset)
954
1080
  if metadata_type == definitions.DatabaseMetaDataKeyType.INDEX_NAMES:
955
- return IndexNamesKey.FromDecoder(
956
- decoder, key_prefix, base_offset)
957
- raise errors.ParserError(f'unknown database metadata type {metadata_type}.')
1081
+ return IndexNamesKey.FromDecoder(decoder, key_prefix, base_offset)
1082
+ raise errors.ParserError(f"unknown database metadata type {metadata_type}.")
958
1083
 
959
1084
 
960
1085
  @dataclass
@@ -965,58 +1090,78 @@ class ObjectStoreMetaDataKey(BaseIndexedDBKey):
965
1090
  object_store_id: the ID of the object store.
966
1091
  metadata_type: the object store metadata type.
967
1092
  """
1093
+
968
1094
  object_store_id: int
969
1095
  metadata_type: definitions.ObjectStoreMetaDataKeyType
970
1096
 
971
1097
  def DecodeValue(
972
- self,
973
- decoder: utils.LevelDBDecoder
1098
+ self, decoder: utils.LevelDBDecoder
974
1099
  ) -> Union[IDBKeyPath, str, bool, int]:
975
1100
  """Decodes the object store metadata value."""
976
- if (self.metadata_type ==
977
- definitions.ObjectStoreMetaDataKeyType.OBJECT_STORE_NAME):
1101
+ if (
1102
+ self.metadata_type
1103
+ == definitions.ObjectStoreMetaDataKeyType.OBJECT_STORE_NAME
1104
+ ):
978
1105
  return decoder.DecodeString()[1]
979
1106
  if self.metadata_type == definitions.ObjectStoreMetaDataKeyType.KEY_PATH:
980
1107
  return IDBKeyPath.FromDecoder(decoder)
981
- if (self.metadata_type ==
982
- definitions.ObjectStoreMetaDataKeyType.AUTO_INCREMENT_FLAG):
1108
+ if (
1109
+ self.metadata_type
1110
+ == definitions.ObjectStoreMetaDataKeyType.AUTO_INCREMENT_FLAG
1111
+ ):
983
1112
  return decoder.DecodeBool()[1]
984
- if (self.metadata_type ==
985
- definitions.ObjectStoreMetaDataKeyType.IS_EVICTABLE):
1113
+ if (
1114
+ self.metadata_type
1115
+ == definitions.ObjectStoreMetaDataKeyType.IS_EVICTABLE
1116
+ ):
986
1117
  return decoder.DecodeBool()[1]
987
- if (self.metadata_type ==
988
- definitions.ObjectStoreMetaDataKeyType.LAST_VERSION_NUMBER):
1118
+ if (
1119
+ self.metadata_type
1120
+ == definitions.ObjectStoreMetaDataKeyType.LAST_VERSION_NUMBER
1121
+ ):
989
1122
  return decoder.DecodeInt(signed=False)[1]
990
- if (self.metadata_type ==
991
- definitions.ObjectStoreMetaDataKeyType.MAXIMUM_ALLOCATED_INDEX_ID):
1123
+ if (
1124
+ self.metadata_type
1125
+ == definitions.ObjectStoreMetaDataKeyType.MAXIMUM_ALLOCATED_INDEX_ID
1126
+ ):
992
1127
  return decoder.DecodeInt()[1]
993
- if (self.metadata_type ==
994
- definitions.ObjectStoreMetaDataKeyType.HAS_KEY_PATH):
1128
+ if (
1129
+ self.metadata_type
1130
+ == definitions.ObjectStoreMetaDataKeyType.HAS_KEY_PATH
1131
+ ):
995
1132
  return decoder.DecodeBool()[1]
996
- if (self.metadata_type ==
997
- definitions.ObjectStoreMetaDataKeyType.KEY_GENERATOR_CURRENT_NUMBER):
1133
+ if (
1134
+ self.metadata_type
1135
+ == definitions.ObjectStoreMetaDataKeyType.KEY_GENERATOR_CURRENT_NUMBER
1136
+ ):
998
1137
  return decoder.DecodeInt()[1]
999
- raise errors.ParserError(f'Unknown metadata type {self.metadata_type}')
1138
+ raise errors.ParserError(f"Unknown metadata type {self.metadata_type}")
1000
1139
 
1001
1140
  @classmethod
1002
1141
  def FromDecoder(
1003
1142
  cls,
1004
1143
  decoder: utils.LevelDBDecoder,
1005
1144
  key_prefix: KeyPrefix,
1006
- base_offset: int = 0
1145
+ base_offset: int = 0,
1007
1146
  ) -> ObjectStoreMetaDataKey:
1008
1147
  """Decodes the object store metadata key."""
1009
1148
  offset, metadata_value = decoder.DecodeUint8()
1010
- if (metadata_value !=
1011
- definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA):
1012
- raise errors.ParserError('Not a ObjectStoreMetaDataKey')
1149
+ if (
1150
+ metadata_value
1151
+ != definitions.DatabaseMetaDataKeyType.OBJECT_STORE_META_DATA
1152
+ ):
1153
+ raise errors.ParserError("Not a ObjectStoreMetaDataKey")
1013
1154
 
1014
1155
  _, object_store_id = decoder.DecodeVarint()
1015
1156
  _, metadata_value = decoder.DecodeUint8()
1016
1157
  metadata_type = definitions.ObjectStoreMetaDataKeyType(metadata_value)
1017
1158
  return cls(
1018
- offset=base_offset + offset, key_prefix=key_prefix,
1019
- object_store_id=object_store_id, metadata_type=metadata_type)
1159
+ offset=base_offset + offset,
1160
+ key_prefix=key_prefix,
1161
+ object_store_id=object_store_id,
1162
+ metadata_type=metadata_type,
1163
+ )
1164
+
1020
1165
 
1021
1166
  @dataclass
1022
1167
  class ObjectStoreDataValue:
@@ -1024,13 +1169,12 @@ class ObjectStoreDataValue:
1024
1169
 
1025
1170
  Attributes:
1026
1171
  version: the version prefix.
1027
- is_wrapped: True if the value was wrapped.
1028
1172
  blob_size: the blob size, only valid if wrapped.
1029
1173
  blob_offset: the blob offset, only valid if wrapped.
1030
1174
  value: the blink serialized value, only valid if not wrapped.
1031
1175
  """
1176
+
1032
1177
  version: int
1033
- is_wrapped: bool
1034
1178
  blob_size: Optional[int]
1035
1179
  blob_offset: Optional[int]
1036
1180
  value: Any
@@ -1043,68 +1187,59 @@ class ObjectStoreDataKey(BaseIndexedDBKey):
1043
1187
  Attributes:
1044
1188
  encoded_user_key: the encoded user key.
1045
1189
  """
1190
+
1046
1191
  encoded_user_key: IDBKey
1047
1192
 
1048
- def DecodeValue(
1049
- self,
1050
- decoder: utils.LevelDBDecoder
1051
- ) -> ObjectStoreDataValue:
1193
+ def DecodeValue(self, decoder: utils.LevelDBDecoder) -> ObjectStoreDataValue:
1052
1194
  """Decodes the object store data value."""
1053
1195
  _, version = decoder.DecodeVarint()
1054
1196
 
1055
1197
  _, wrapped_header_bytes = decoder.PeekBytes(3)
1056
1198
  if len(wrapped_header_bytes) != 3:
1057
- raise errors.DecoderError('Insufficient bytes')
1199
+ raise errors.DecoderError("Insufficient bytes")
1058
1200
 
1059
- if (wrapped_header_bytes[0] ==
1060
- definitions.BlinkSerializationTag.VERSION and
1061
- wrapped_header_bytes[1] ==
1062
- definitions.REQUIRES_PROCESSING_SSV_PSEUDO_VERSION and
1063
- wrapped_header_bytes[2] == definitions.REPLACE_WITH_BLOB):
1201
+ if (
1202
+ wrapped_header_bytes[0] == definitions.BlinkSerializationTag.VERSION
1203
+ and wrapped_header_bytes[1]
1204
+ == definitions.REQUIRES_PROCESSING_SSV_PSEUDO_VERSION
1205
+ and wrapped_header_bytes[2] == definitions.REPLACE_WITH_BLOB
1206
+ ):
1064
1207
  _ = decoder.ReadBytes(3)
1065
1208
  _, blob_size = decoder.DecodeVarint()
1066
1209
  _, blob_offset = decoder.DecodeVarint()
1067
1210
  return ObjectStoreDataValue(
1068
1211
  version=version,
1069
- is_wrapped=True,
1070
1212
  blob_size=blob_size,
1071
1213
  blob_offset=blob_offset,
1072
- value=None)
1214
+ value=None,
1215
+ )
1216
+
1073
1217
  _, blink_bytes = decoder.ReadBytes()
1074
- is_wrapped = False
1075
- if (
1076
- wrapped_header_bytes[0] ==
1077
- definitions.BlinkSerializationTag.VERSION and
1078
- wrapped_header_bytes[1] ==
1079
- definitions.REQUIRES_PROCESSING_SSV_PSEUDO_VERSION and
1080
- wrapped_header_bytes[2] == definitions.COMPRESSED_WITH_SNAPPY):
1081
- is_wrapped = True
1082
- # ignore the wrapped header bytes when decompressing
1083
- blink_bytes = snappy.decompress(blink_bytes[3:])
1084
1218
  blink_value = blink.V8ScriptValueDecoder.FromBytes(blink_bytes)
1085
1219
  return ObjectStoreDataValue(
1086
- version=version,
1087
- is_wrapped=is_wrapped,
1088
- blob_size=None,
1089
- blob_offset=None,
1090
- value=blink_value)
1220
+ version=version, blob_size=None, blob_offset=None, value=blink_value
1221
+ )
1091
1222
 
1092
1223
  @classmethod
1093
1224
  def FromDecoder(
1094
1225
  cls,
1095
1226
  decoder: utils.LevelDBDecoder,
1096
1227
  key_prefix: KeyPrefix,
1097
- base_offset: int = 0
1228
+ base_offset: int = 0,
1098
1229
  ) -> ObjectStoreDataKey:
1099
1230
  """Decodes the object store data key."""
1100
- if (key_prefix.GetKeyPrefixType() !=
1101
- definitions.KeyPrefixType.OBJECT_STORE_DATA):
1102
- raise errors.ParserError('Invalid KeyPrefix for ObjectStoreDataKey')
1231
+ if (
1232
+ key_prefix.GetKeyPrefixType()
1233
+ != definitions.KeyPrefixType.OBJECT_STORE_DATA
1234
+ ):
1235
+ raise errors.ParserError("Invalid KeyPrefix for ObjectStoreDataKey")
1103
1236
  offset = decoder.stream.tell()
1104
1237
  encoded_user_key = IDBKey.FromDecoder(decoder, offset)
1105
1238
  return cls(
1106
1239
  offset=base_offset + offset,
1107
- key_prefix=key_prefix, encoded_user_key=encoded_user_key)
1240
+ key_prefix=key_prefix,
1241
+ encoded_user_key=encoded_user_key,
1242
+ )
1108
1243
 
1109
1244
 
1110
1245
  @dataclass
@@ -1114,26 +1249,29 @@ class ExistsEntryKey(BaseIndexedDBKey):
1114
1249
  Attributes:
1115
1250
  encoded_user_key: the encoded user key.
1116
1251
  """
1252
+
1117
1253
  encoded_user_key: IDBKey
1118
1254
 
1119
1255
  def DecodeValue(self, decoder: utils.LevelDBDecoder) -> int:
1120
1256
  """Decodes the exists entry value."""
1121
1257
  _, data = decoder.ReadBytes()
1122
- return int.from_bytes(data, byteorder='little', signed=False)
1258
+ return int.from_bytes(data, byteorder="little", signed=False)
1123
1259
 
1124
1260
  @classmethod
1125
1261
  def FromDecoder(
1126
1262
  cls,
1127
1263
  decoder: utils.LevelDBDecoder,
1128
1264
  key_prefix: KeyPrefix,
1129
- base_offset: int = 0
1265
+ base_offset: int = 0,
1130
1266
  ) -> ExistsEntryKey:
1131
1267
  """Decodes the exists entry key."""
1132
1268
  offset = decoder.stream.tell()
1133
1269
  encoded_user_key = IDBKey.FromDecoder(decoder, offset)
1134
1270
  return cls(
1135
1271
  offset=base_offset + offset,
1136
- key_prefix=key_prefix, encoded_user_key=encoded_user_key)
1272
+ key_prefix=key_prefix,
1273
+ encoded_user_key=encoded_user_key,
1274
+ )
1137
1275
 
1138
1276
 
1139
1277
  @dataclass
@@ -1145,6 +1283,7 @@ class IndexDataKey(BaseIndexedDBKey):
1145
1283
  sequence_number: the sequence number of the data key.
1146
1284
  encoded_primary_key: the encoded primary key.
1147
1285
  """
1286
+
1148
1287
  encoded_user_key: IDBKey
1149
1288
  sequence_number: Optional[int]
1150
1289
  encoded_primary_key: Optional[IDBKey]
@@ -1160,7 +1299,7 @@ class IndexDataKey(BaseIndexedDBKey):
1160
1299
  cls,
1161
1300
  decoder: utils.LevelDBDecoder,
1162
1301
  key_prefix: KeyPrefix,
1163
- base_offset: int = 0
1302
+ base_offset: int = 0,
1164
1303
  ) -> IndexDataKey:
1165
1304
  """Decodes the index data key."""
1166
1305
  offset = decoder.stream.tell()
@@ -1174,7 +1313,8 @@ class IndexDataKey(BaseIndexedDBKey):
1174
1313
  if decoder.NumRemainingBytes() > 0:
1175
1314
  encoded_primary_key_offset = decoder.stream.tell()
1176
1315
  encoded_primary_key = IDBKey.FromDecoder(
1177
- decoder, encoded_primary_key_offset)
1316
+ decoder, encoded_primary_key_offset
1317
+ )
1178
1318
  else:
1179
1319
  encoded_primary_key = None
1180
1320
 
@@ -1183,7 +1323,8 @@ class IndexDataKey(BaseIndexedDBKey):
1183
1323
  encoded_user_key=encoded_user_key,
1184
1324
  sequence_number=sequence_number,
1185
1325
  encoded_primary_key=encoded_primary_key,
1186
- offset=base_offset + offset)
1326
+ offset=base_offset + offset,
1327
+ )
1187
1328
 
1188
1329
 
1189
1330
  @dataclass
@@ -1193,11 +1334,11 @@ class BlobEntryKey(BaseIndexedDBKey):
1193
1334
  Attributes:
1194
1335
  user_key: the user/primary key.
1195
1336
  """
1337
+
1196
1338
  user_key: IDBKey
1197
1339
 
1198
1340
  def DecodeValue(
1199
- self,
1200
- decoder: utils.LevelDBDecoder
1341
+ self, decoder: utils.LevelDBDecoder
1201
1342
  ) -> IndexedDBExternalObject:
1202
1343
  """Decodes the blob entry value."""
1203
1344
  return IndexedDBExternalObject.FromDecoder(decoder)
@@ -1207,14 +1348,15 @@ class BlobEntryKey(BaseIndexedDBKey):
1207
1348
  cls,
1208
1349
  decoder: utils.LevelDBDecoder,
1209
1350
  key_prefix: KeyPrefix,
1210
- base_offset: int = 0
1351
+ base_offset: int = 0,
1211
1352
  ) -> BlobEntryKey:
1212
1353
  """Decodes the blob entry key."""
1213
1354
  offset = decoder.stream.tell()
1214
1355
  user_key = IDBKey.FromDecoder(decoder, offset)
1215
1356
 
1216
- return cls(key_prefix=key_prefix, user_key=user_key,
1217
- offset=base_offset + offset)
1357
+ return cls(
1358
+ key_prefix=key_prefix, user_key=user_key, offset=base_offset + offset
1359
+ )
1218
1360
 
1219
1361
 
1220
1362
  @dataclass
@@ -1224,7 +1366,11 @@ class IndexedDbKey(BaseIndexedDBKey):
1224
1366
  A factory class for parsing IndexedDB keys.
1225
1367
  """
1226
1368
 
1227
- METADATA_TYPE_TO_CLASS = {
1369
+ METADATA_TYPE_TO_CLASS: ClassVar[
1370
+ dict[ # pylint: disable=invalid-name
1371
+ definitions.KeyPrefixType, Optional[type[BaseIndexedDBKey]]
1372
+ ]
1373
+ ] = {
1228
1374
  definitions.KeyPrefixType.BLOB_ENTRY: BlobEntryKey,
1229
1375
  definitions.KeyPrefixType.DATABASE_METADATA: DatabaseMetaDataKey,
1230
1376
  definitions.KeyPrefixType.EXISTS_ENTRY: ExistsEntryKey,
@@ -1246,23 +1392,18 @@ class IndexedDbKey(BaseIndexedDBKey):
1246
1392
  cls,
1247
1393
  decoder: utils.LevelDBDecoder,
1248
1394
  key_prefix: KeyPrefix,
1249
- base_offset: int = 0
1250
- ) -> Union[
1251
- BlobEntryKey,
1252
- DatabaseMetaDataKey,
1253
- ExistsEntryKey,
1254
- GlobalMetaDataKey,
1255
- IndexDataKey,
1256
- ObjectStoreDataKey]:
1395
+ base_offset: int = 0,
1396
+ ) -> BaseIndexedDBKey:
1257
1397
  """Decodes the IndexedDB key."""
1258
1398
  key_type = key_prefix.GetKeyPrefixType()
1259
1399
  key_class = cls.METADATA_TYPE_TO_CLASS.get(key_type)
1260
1400
  if not key_class:
1261
- raise errors.ParserError('Unknown KeyPrefixType')
1401
+ raise errors.ParserError("Unknown KeyPrefixType")
1262
1402
  return key_class.FromDecoder(
1263
1403
  decoder=decoder,
1264
- key_prefix=key_prefix, #pytype: disable=bad-return-type
1265
- base_offset=base_offset)
1404
+ key_prefix=key_prefix,
1405
+ base_offset=base_offset,
1406
+ )
1266
1407
 
1267
1408
 
1268
1409
  @dataclass
@@ -1274,45 +1415,49 @@ class IndexMetaDataKey(BaseIndexedDBKey):
1274
1415
  index_id: the index ID.
1275
1416
  metadata_type: the metadata key type.
1276
1417
  """
1418
+
1277
1419
  object_store_id: int
1278
1420
  index_id: int
1279
1421
  metadata_type: definitions.IndexMetaDataKeyType
1280
1422
 
1281
1423
  def DecodeValue(
1282
- self,
1283
- decoder: utils.LevelDBDecoder
1424
+ self, decoder: utils.LevelDBDecoder
1284
1425
  ) -> Union[bool, IDBKeyPath, str]:
1285
1426
  """Decodes the index metadata value."""
1286
1427
  if self.metadata_type == definitions.IndexMetaDataKeyType.INDEX_NAME:
1287
1428
  return decoder.DecodeString()[1]
1288
1429
  if self.metadata_type == definitions.IndexMetaDataKeyType.KEY_PATH:
1289
1430
  return IDBKeyPath.FromDecoder(decoder)
1290
- if (self.metadata_type ==
1291
- definitions.IndexMetaDataKeyType.MULTI_ENTRY_FLAG):
1431
+ if self.metadata_type == definitions.IndexMetaDataKeyType.MULTI_ENTRY_FLAG:
1292
1432
  return decoder.DecodeBool()[1]
1293
1433
  if self.metadata_type == definitions.IndexMetaDataKeyType.UNIQUE_FLAG:
1294
1434
  return decoder.DecodeBool()[1]
1295
1435
  raise errors.ParserError(
1296
- f'Unknown index metadata type {self.metadata_type}')
1436
+ f"Unknown index metadata type {self.metadata_type}"
1437
+ )
1297
1438
 
1298
1439
  @classmethod
1299
1440
  def FromDecoder(
1300
1441
  cls,
1301
1442
  decoder: utils.LevelDBDecoder,
1302
1443
  key_prefix: KeyPrefix,
1303
- base_offset: int = 0
1444
+ base_offset: int = 0,
1304
1445
  ) -> IndexMetaDataKey:
1305
1446
  """Decodes the index metadata key."""
1306
1447
  offset, key_type = decoder.DecodeUint8()
1307
1448
  if key_type != definitions.DatabaseMetaDataKeyType.INDEX_META_DATA:
1308
- raise errors.ParserError('Not an IndexMetaDataKey.')
1449
+ raise errors.ParserError("Not an IndexMetaDataKey.")
1309
1450
  _, object_store_id = decoder.DecodeVarint()
1310
1451
  _, index_id = decoder.DecodeVarint()
1311
1452
  _, metadata_bytes = decoder.ReadBytes(1)
1312
1453
  metadata_type = definitions.IndexMetaDataKeyType(metadata_bytes[0])
1313
- return cls(offset=base_offset + offset, key_prefix=key_prefix,
1314
- object_store_id=object_store_id, index_id=index_id,
1315
- metadata_type=metadata_type)
1454
+ return cls(
1455
+ offset=base_offset + offset,
1456
+ key_prefix=key_prefix,
1457
+ object_store_id=object_store_id,
1458
+ index_id=index_id,
1459
+ metadata_type=metadata_type,
1460
+ )
1316
1461
 
1317
1462
 
1318
1463
  @dataclass
@@ -1331,6 +1476,7 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
1331
1476
  last_modified: the last modified time if a blob or file, None otherwise.
1332
1477
  token: the token if a filesystem access handle, None otherwise.
1333
1478
  """
1479
+
1334
1480
  offset: int = field(compare=False)
1335
1481
  object_type: definitions.ExternalObjectType
1336
1482
  blob_number: Optional[int]
@@ -1342,16 +1488,15 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
1342
1488
 
1343
1489
  @classmethod
1344
1490
  def FromDecoder(
1345
- cls,
1346
- decoder: utils.LevelDBDecoder,
1347
- base_offset: int = 0
1491
+ cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
1348
1492
  ) -> ExternalObjectEntry:
1349
1493
  """Decodes the external object entry."""
1350
1494
  offset, object_type_value = decoder.DecodeUint8()
1351
1495
  object_type = definitions.ExternalObjectType(object_type_value)
1352
- if (object_type in
1353
- (definitions.ExternalObjectType.BLOB,
1354
- definitions.ExternalObjectType.FILE)):
1496
+ if object_type in (
1497
+ definitions.ExternalObjectType.BLOB,
1498
+ definitions.ExternalObjectType.FILE,
1499
+ ):
1355
1500
  _, blob_number = decoder.DecodeVarint()
1356
1501
  _, mime_type = decoder.DecodeStringWithLength()
1357
1502
  _, size = decoder.DecodeVarint()
@@ -1363,8 +1508,10 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
1363
1508
  last_modified = None
1364
1509
  token = None
1365
1510
  else:
1366
- if (object_type ==
1367
- definitions.ExternalObjectType.FILE_SYSTEM_ACCESS_HANDLE):
1511
+ if (
1512
+ object_type
1513
+ == definitions.ExternalObjectType.FILE_SYSTEM_ACCESS_HANDLE
1514
+ ):
1368
1515
  _, token = decoder.DecodeBlobWithLength()
1369
1516
  else:
1370
1517
  token = None
@@ -1374,9 +1521,16 @@ class ExternalObjectEntry(utils.FromDecoderMixin):
1374
1521
  filename = None
1375
1522
  last_modified = None
1376
1523
 
1377
- return cls(offset=base_offset + offset, object_type=object_type,
1378
- blob_number=blob_number, mime_type=mime_type, size=size,
1379
- filename=filename, last_modified=last_modified, token=token)
1524
+ return cls(
1525
+ offset=base_offset + offset,
1526
+ object_type=object_type,
1527
+ blob_number=blob_number,
1528
+ mime_type=mime_type,
1529
+ size=size,
1530
+ filename=filename,
1531
+ last_modified=last_modified,
1532
+ token=token,
1533
+ )
1380
1534
 
1381
1535
 
1382
1536
  @dataclass
@@ -1387,14 +1541,13 @@ class IndexedDBExternalObject(utils.FromDecoderMixin):
1387
1541
  offset: the offset of the IndexedDBExternalObject.
1388
1542
  entries: a list of external objects.
1389
1543
  """
1544
+
1390
1545
  offset: int = field(compare=False)
1391
1546
  entries: list[ExternalObjectEntry]
1392
1547
 
1393
1548
  @classmethod
1394
1549
  def FromDecoder(
1395
- cls,
1396
- decoder: utils.LevelDBDecoder,
1397
- base_offset: int = 0
1550
+ cls, decoder: utils.LevelDBDecoder, base_offset: int = 0
1398
1551
  ) -> IndexedDBExternalObject:
1399
1552
  """Decodes the external object."""
1400
1553
  entries = []
@@ -1407,22 +1560,8 @@ class IndexedDBExternalObject(utils.FromDecoderMixin):
1407
1560
 
1408
1561
 
1409
1562
  @dataclass
1410
- class ObjectStore:
1411
- """An IndexedDB Object Store.
1412
-
1413
- Attributes:
1414
- id: the object store ID.
1415
- name: the object store name.
1416
- records: the records in the object store.
1417
- """
1418
- id: int
1419
- name: str
1420
- records: list = field(default_factory=list, repr=False)
1421
-
1422
-
1423
- @dataclass
1424
- class IndexedDBRecord:
1425
- """An IndexedDB Record.
1563
+ class ChromiumIndexedDBRecord:
1564
+ """An IndexedDB Record parsed from LevelDB.
1426
1565
 
1427
1566
  Attributes:
1428
1567
  path: the source file path
@@ -1434,7 +1573,15 @@ class IndexedDBRecord:
1434
1573
  level: the leveldb level, if applicable, None can indicate the record
1435
1574
  originated from a log file or the level could not be determined.
1436
1575
  recovered: True if the record is a recovered record.
1576
+ database_id: the database ID.
1577
+ object_store_id: the object store ID.
1578
+ database_name: the name of the database, if available.
1579
+ object_store_name: the name of the object store, if available.
1580
+ blobs: the list of blob paths and contents or error message, if available.
1581
+ raw_key: the raw key, if available.
1582
+ raw_value: the raw value, if available.
1437
1583
  """
1584
+
1438
1585
  path: str
1439
1586
  offset: int
1440
1587
  key: Any
@@ -1443,46 +1590,181 @@ class IndexedDBRecord:
1443
1590
  type: int
1444
1591
  level: Optional[int]
1445
1592
  recovered: Optional[bool]
1593
+ database_id: int
1594
+ object_store_id: int
1595
+ database_name: Optional[str] = None
1596
+ object_store_name: Optional[str] = None
1597
+ blobs: Optional[list[tuple[str, Optional[Any]]]] = None
1598
+ raw_key: Optional[bytes] = None
1599
+ raw_value: Optional[bytes] = None
1446
1600
 
1447
1601
  @classmethod
1448
1602
  def FromLevelDBRecord(
1449
1603
  cls,
1450
- db_record: record.LevelDBRecord
1451
- ) -> IndexedDBRecord:
1452
- """Returns an IndexedDBRecord from a ParsedInternalKey."""
1604
+ db_record: record.LevelDBRecord,
1605
+ parse_value: bool = True,
1606
+ include_raw_data: bool = False,
1607
+ blob_folder_reader: Optional[BlobFolderReader] = None,
1608
+ ) -> ChromiumIndexedDBRecord:
1609
+ """Returns an ChromiumIndexedDBRecord from a ParsedInternalKey."""
1453
1610
  idb_key = IndexedDbKey.FromBytes(
1454
- db_record.record.key, base_offset=db_record.record.offset)
1455
- idb_value = idb_key.ParseValue(db_record.record.value)
1611
+ db_record.record.key, base_offset=db_record.record.offset
1612
+ )
1613
+
1614
+ if parse_value:
1615
+ idb_value = idb_key.ParseValue(db_record.record.value)
1616
+ else:
1617
+ idb_value = None
1618
+
1619
+ blobs = []
1620
+ if isinstance(idb_value, IndexedDBExternalObject) and blob_folder_reader:
1621
+ for (
1622
+ blob_path_or_error,
1623
+ blob_data,
1624
+ ) in blob_folder_reader.ReadBlobsFromExternalObjectEntries(
1625
+ idb_key.key_prefix.database_id, idb_value.entries
1626
+ ):
1627
+ if blob_data:
1628
+ blob = blink.V8ScriptValueDecoder.FromBytes(blob_data)
1629
+ else:
1630
+ blob = None
1631
+ blobs.append((blob_path_or_error, blob))
1632
+
1456
1633
  return cls(
1457
1634
  path=db_record.path,
1458
1635
  offset=db_record.record.offset,
1459
1636
  key=idb_key,
1460
1637
  value=idb_value,
1461
- sequence_number=db_record.record.sequence_number if hasattr(
1462
- db_record.record, 'sequence_number') else None,
1638
+ sequence_number=(
1639
+ db_record.record.sequence_number
1640
+ if hasattr(db_record.record, "sequence_number")
1641
+ else None
1642
+ ),
1463
1643
  type=db_record.record.record_type,
1464
1644
  level=db_record.level,
1465
- recovered=db_record.recovered)
1645
+ recovered=db_record.recovered,
1646
+ database_id=idb_key.key_prefix.database_id,
1647
+ object_store_id=idb_key.key_prefix.object_store_id,
1648
+ database_name=None,
1649
+ object_store_name=None,
1650
+ blobs=blobs,
1651
+ raw_key=db_record.record.key if include_raw_data else None,
1652
+ raw_value=db_record.record.value if include_raw_data else None,
1653
+ )
1466
1654
 
1467
1655
  @classmethod
1468
1656
  def FromFile(
1469
1657
  cls,
1470
- file_path: pathlib.Path
1471
- ) -> Generator[IndexedDBRecord, None, None]:
1472
- """Yields IndexedDBRecords from a file."""
1658
+ file_path: pathlib.Path,
1659
+ parse_value: bool = True,
1660
+ include_raw_data: bool = False,
1661
+ blob_folder_reader: Optional[BlobFolderReader] = None,
1662
+ ) -> Generator[ChromiumIndexedDBRecord, None, None]:
1663
+ """Yields ChromiumIndexedDBRecord from a file."""
1473
1664
  for db_record in record.LevelDBRecord.FromFile(file_path):
1474
1665
  try:
1475
- yield cls.FromLevelDBRecord(db_record)
1476
- except(
1666
+ yield cls.FromLevelDBRecord(
1667
+ db_record,
1668
+ parse_value=parse_value,
1669
+ include_raw_data=include_raw_data,
1670
+ blob_folder_reader=blob_folder_reader,
1671
+ )
1672
+ except (
1477
1673
  errors.ParserError,
1478
1674
  errors.DecoderError,
1479
- NotImplementedError) as err:
1480
- print((
1481
- 'Error parsing Indexeddb record: '
1482
- f'{err} at offset {db_record.record.offset} in '
1483
- f'{db_record.path}'),
1484
- file=sys.stderr)
1485
- print(f'Traceback: {traceback.format_exc()}', file=sys.stderr)
1675
+ NotImplementedError,
1676
+ ) as err:
1677
+ print(
1678
+ (
1679
+ "Error parsing Indexeddb record: "
1680
+ f"{err} at offset {db_record.record.offset} in "
1681
+ f"{db_record.path}"
1682
+ ),
1683
+ file=sys.stderr,
1684
+ )
1685
+ print(f"Traceback: {traceback.format_exc()}", file=sys.stderr)
1686
+
1687
+
1688
+ class BlobFolderReader:
1689
+ """A blob folder reader for Chrome/Chromium.
1690
+
1691
+ Attributes:
1692
+ folder_name (str): the source blob folder.
1693
+ """
1694
+
1695
+ def __init__(self, folder_name: pathlib.Path):
1696
+ """Initializes the BlobFolderReader.
1697
+
1698
+ Args:
1699
+ folder_name: the source blob folder.
1700
+
1701
+ Raises:
1702
+ ValueError: if folder_name is None or not a directory.
1703
+ """
1704
+ if not folder_name or not folder_name.is_dir():
1705
+ raise ValueError(f"{folder_name} is None or not a directory")
1706
+ self.folder_name = folder_name.absolute()
1707
+
1708
+ def ReadBlob(self, database_id: int, blob_id: int) -> tuple[str, bytes]:
1709
+ """Reads a blob from the blob folder.
1710
+
1711
+ Args:
1712
+ database_id: the database id of the blob to read.
1713
+ blob_id: the blob id to read.
1714
+
1715
+ Returns:
1716
+ A tuple of the blob path and contents.
1717
+
1718
+ Raises:
1719
+ FileNotFoundError: if the database directory or blob folder or blob not
1720
+ found.
1721
+ """
1722
+ directory_path = self.folder_name / f"{database_id:x}"
1723
+ if not directory_path.exists():
1724
+ raise FileNotFoundError(f"Database directory not found: {directory_path}")
1725
+
1726
+ blob_folder = directory_path / f"{(blob_id & 0xff00) >> 8:02x}"
1727
+ if not blob_folder.exists():
1728
+ raise FileNotFoundError(f"Blob folder not found: {blob_folder}")
1729
+
1730
+ blob_path = blob_folder / f"{blob_id:x}"
1731
+ if not blob_path.exists():
1732
+ raise FileNotFoundError(f"Blob ({blob_id}) not found: {blob_path}")
1733
+
1734
+ with open(blob_path, "rb") as f:
1735
+ return str(blob_path), f.read()
1736
+
1737
+ def ReadBlobsFromExternalObjectEntries(
1738
+ self, database_id: int, entries: list[ExternalObjectEntry]
1739
+ ) -> Generator[tuple[str, Optional[bytes]], None, None]:
1740
+ """Reads blobs from the blob folder.
1741
+
1742
+ Args:
1743
+ database_id: the database id.
1744
+ entries: the external object entries.
1745
+
1746
+ Yields:
1747
+ A tuple of blob path and contents or if the blob is not found, an error
1748
+ message and None.
1749
+ """
1750
+ for entry in entries:
1751
+ if (
1752
+ entry.object_type
1753
+ in (
1754
+ definitions.ExternalObjectType.BLOB,
1755
+ definitions.ExternalObjectType.FILE,
1756
+ )
1757
+ and entry.blob_number is not None
1758
+ ):
1759
+ try:
1760
+ yield self.ReadBlob(database_id, entry.blob_number)
1761
+ except FileNotFoundError as err:
1762
+ error_message = (
1763
+ f"Blob not found for ExternalObjectEntry at offset {entry.offset}"
1764
+ f": {err}"
1765
+ )
1766
+ print(error_message, file=sys.stderr)
1767
+ yield error_message, None
1486
1768
 
1487
1769
 
1488
1770
  class FolderReader:
@@ -1502,36 +1784,59 @@ class FolderReader:
1502
1784
  ValueError: if folder_name is None or not a directory.
1503
1785
  """
1504
1786
  if not folder_name or not folder_name.is_dir():
1505
- raise ValueError(f'{folder_name} is None or not a directory')
1506
- self.folder_name = folder_name
1787
+ raise ValueError(f"{folder_name} is None or not a directory")
1788
+ self.folder_name = folder_name.absolute()
1789
+
1790
+ # Locate the correponding blob folder. The folder_name should be
1791
+ # <origin>.leveldb and the blob folder should be <origin>.blob
1792
+ if str(self.folder_name).endswith(".leveldb"):
1793
+ self.blob_folder_reader = BlobFolderReader(
1794
+ pathlib.Path(str(self.folder_name).replace(".leveldb", ".blob"))
1795
+ )
1796
+ else:
1797
+ self.blob_folder_reader = None # type: ignore[assignment]
1507
1798
 
1508
1799
  def GetRecords(
1509
1800
  self,
1510
1801
  use_manifest: bool = False,
1511
- use_sequence_number: bool = False
1512
- ) -> Generator[IndexedDBRecord, None, None]:
1513
- """Yield LevelDBRecords.
1802
+ use_sequence_number: bool = False,
1803
+ parse_value: bool = True,
1804
+ include_raw_data: bool = False,
1805
+ ) -> Generator[ChromiumIndexedDBRecord, None, None]:
1806
+ """Yields ChromiumIndexedDBRecord.
1514
1807
 
1515
1808
  Args:
1516
1809
  use_manifest: True to use the current manifest in the folder as a means to
1517
1810
  find the active file set.
1518
1811
  use_sequence_number: True to use the sequence number to determine the
1812
+ recovered field.
1813
+ parse_value: True to parse values.
1814
+
1519
1815
  Yields:
1520
- IndexedDBRecord.
1816
+ ChromiumIndexedDBRecord.
1521
1817
  """
1522
1818
  leveldb_folder_reader = record.FolderReader(self.folder_name)
1523
1819
  for leveldb_record in leveldb_folder_reader.GetRecords(
1524
- use_manifest=use_manifest,
1525
- use_sequence_number=use_sequence_number):
1820
+ use_manifest=use_manifest, use_sequence_number=use_sequence_number
1821
+ ):
1526
1822
  try:
1527
- yield IndexedDBRecord.FromLevelDBRecord(leveldb_record)
1528
- except(
1823
+ yield ChromiumIndexedDBRecord.FromLevelDBRecord(
1824
+ leveldb_record,
1825
+ parse_value=parse_value,
1826
+ include_raw_data=include_raw_data,
1827
+ blob_folder_reader=self.blob_folder_reader,
1828
+ )
1829
+ except (
1529
1830
  errors.ParserError,
1530
1831
  errors.DecoderError,
1531
- NotImplementedError) as err:
1532
- print((
1533
- 'Error parsing Indexeddb record: '
1534
- f'{err} at offset {leveldb_record.record.offset} in '
1535
- f'{leveldb_record.path}'),
1536
- file=sys.stderr)
1537
- print(f'Traceback: {traceback.format_exc()}', file=sys.stderr)
1832
+ NotImplementedError,
1833
+ ) as err:
1834
+ print(
1835
+ (
1836
+ "Error parsing Indexeddb record: "
1837
+ f"{err} at offset {leveldb_record.record.offset} in "
1838
+ f"{leveldb_record.path}"
1839
+ ),
1840
+ file=sys.stderr,
1841
+ )
1842
+ print(f"Traceback: {traceback.format_exc()}", file=sys.stderr)