Skip to content

Commit b4890e2

Browse files
committed
Add a DynamicBuffer for faster VFS implementations.
1 parent d7cbbe9 commit b4890e2

File tree

3 files changed

+111
-24
lines changed

3 files changed

+111
-24
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import 'dart:typed_data';
2+
3+
/// A utility class that manages a growing byte buffer.
4+
/// It dynamically increases its capacity in factors of 2 when needed.
5+
class DynamicBuffer {
6+
Uint8List _buffer;
7+
int _capacity;
8+
int _length = 0;
9+
10+
/// Creates a [DynamicBuffer] with an optional initial capacity.
11+
/// The default initial capacity is 1024 bytes.
12+
DynamicBuffer([int initialCapacity = 1024])
13+
: _capacity = initialCapacity,
14+
_buffer = Uint8List(initialCapacity) {
15+
if (initialCapacity < 1) {
16+
throw ArgumentError("initialCapacity must be positive");
17+
}
18+
}
19+
20+
/// Adds [data] to the buffer, expanding its capacity if necessary.
21+
void add(Uint8List data) {
22+
_ensureCapacity(_length + data.length);
23+
_buffer.setRange(_length, _length + data.length, data);
24+
_length += data.length;
25+
}
26+
27+
/// Writes [data] into the buffer starting at [offset], expanding capacity if necessary.
28+
/// If the write operation extends beyond the current length, the length is updated accordingly.
29+
void write(Uint8List data, int offset) {
30+
if (offset < 0) {
31+
throw ArgumentError("Offset must be non-negative");
32+
}
33+
final endPosition = offset + data.length;
34+
_ensureCapacity(endPosition);
35+
_buffer.setRange(offset, endPosition, data);
36+
if (endPosition > _length) {
37+
_length = endPosition;
38+
}
39+
}
40+
41+
/// Truncates the buffer to a specific [newSize].
42+
///
43+
/// No memory is freed when using [truncate].
44+
///
45+
/// If [newSize] is less than the current length, the buffer is truncated.
46+
/// If [newSize] is greater than the current length, the buffer length is extended with zeros.
47+
void truncate(int newSize) {
48+
if (newSize < 0) {
49+
throw ArgumentError("newSize must be non-negative");
50+
}
51+
_ensureCapacity(newSize);
52+
if (newSize > _length) {
53+
// Zero out the new extended area
54+
_buffer.fillRange(_length, newSize, 0);
55+
}
56+
_length = newSize;
57+
}
58+
59+
/// Returns a [Uint8List] view containing the data up to the current length.
60+
Uint8List toUint8List() {
61+
return Uint8List.view(_buffer.buffer, 0, _length);
62+
}
63+
64+
/// Ensures that the buffer has enough capacity to hold [requiredCapacity] bytes.
65+
void _ensureCapacity(int requiredCapacity) {
66+
if (requiredCapacity > _capacity) {
67+
// Double the capacity until it is sufficient.
68+
int newCapacity = _capacity;
69+
while (newCapacity < requiredCapacity) {
70+
newCapacity *= 2;
71+
}
72+
// Allocate a new buffer and copy existing data.
73+
Uint8List newBuffer = Uint8List(newCapacity);
74+
newBuffer.setRange(0, _length, _buffer);
75+
_buffer = newBuffer;
76+
_capacity = newCapacity;
77+
}
78+
}
79+
80+
/// Returns the current length of the data in the buffer.
81+
int get length => _length;
82+
83+
/// Resets the buffer length to zero without altering its capacity.
84+
void reset() {
85+
_length = 0;
86+
}
87+
}

sqlite3/lib/src/wasm/vfs/indexed_db.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'dart:math';
88
import 'dart:typed_data';
99

1010
import 'package:meta/meta.dart';
11+
import 'package:sqlite3/src/wasm/vfs/dynamic_buffer.dart';
1112
import 'package:web/web.dart' as web;
1213

1314
import '../../constants.dart';
@@ -533,7 +534,11 @@ final class IndexedDbFileSystem extends BaseVirtualFileSystem {
533534
final name = entry.key;
534535
final fileId = entry.value;
535536

536-
_memory.fileData[name] = await _asynchronous.readFully(fileId);
537+
final buffer = DynamicBuffer();
538+
final data = await _asynchronous.readFully(fileId);
539+
buffer.add(data);
540+
541+
_memory.fileData[name] = buffer;
537542
}
538543
}
539544

@@ -642,7 +647,7 @@ class _IndexedDbFile implements VirtualFileSystemFile {
642647
void xWrite(Uint8List buffer, int fileOffset) {
643648
vfs._checkClosed();
644649

645-
final previousContent = vfs._memory.fileData[path] ?? Uint8List(0);
650+
final previousContent = vfs._memory.fileData[path] ?? DynamicBuffer();
646651
memoryFile.xWrite(buffer, fileOffset);
647652

648653
if (!vfs._inMemoryOnlyFiles.contains(path)) {
@@ -651,8 +656,9 @@ class _IndexedDbFile implements VirtualFileSystemFile {
651656
final copy = Uint8List(buffer.length);
652657
copy.setAll(0, buffer);
653658

654-
vfs._submitWork(_WriteFileWorkItem(vfs, path, previousContent)
655-
..writes.add(_OffsetAndBuffer(fileOffset, copy)));
659+
vfs._submitWork(
660+
_WriteFileWorkItem(vfs, path, previousContent.toUint8List())
661+
..writes.add(_OffsetAndBuffer(fileOffset, copy)));
656662
}
657663
}
658664
}

sqlite3/lib/src/wasm/vfs/memory.dart

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import 'dart:math';
22
import 'dart:typed_data';
33

44
import 'package:path/path.dart' as p;
5+
import 'dynamic_buffer.dart';
56

67
import '../../constants.dart';
78
import '../../vfs.dart';
89
import 'utils.dart';
910

1011
final class InMemoryFileSystem extends BaseVirtualFileSystem {
11-
final Map<String, Uint8List?> fileData = {};
12+
final Map<String, DynamicBuffer?> fileData = {};
1213

1314
InMemoryFileSystem({super.name = 'dart-memory', super.random});
1415

@@ -34,7 +35,7 @@ final class InMemoryFileSystem extends BaseVirtualFileSystem {
3435
final create = flags & SqlFlag.SQLITE_OPEN_CREATE;
3536

3637
if (create != 0) {
37-
fileData[pathStr] = Uint8List(0);
38+
fileData[pathStr] = DynamicBuffer();
3839
} else {
3940
throw VfsException(SqlError.SQLITE_CANTOPEN);
4041
}
@@ -69,7 +70,7 @@ class _InMemoryFile extends BaseVfsFile {
6970
if (file == null || file.length <= offset) return 0;
7071

7172
final available = min(buffer.length, file.length - offset);
72-
buffer.setRange(0, available, file, offset);
73+
buffer.setRange(0, available, file.toUint8List(), offset);
7374
return available;
7475
}
7576

@@ -102,12 +103,12 @@ class _InMemoryFile extends BaseVfsFile {
102103
void xTruncate(int size) {
103104
final file = vfs.fileData[path];
104105

105-
final result = Uint8List(size);
106-
if (file != null) {
107-
result.setRange(0, min(size, file.length), file);
106+
if (file == null) {
107+
vfs.fileData[path] = DynamicBuffer();
108+
vfs.fileData[path]!.truncate(size);
109+
} else {
110+
file.truncate(size);
108111
}
109-
110-
vfs.fileData[path] = result;
111112
}
112113

113114
@override
@@ -117,19 +118,12 @@ class _InMemoryFile extends BaseVfsFile {
117118

118119
@override
119120
void xWrite(Uint8List buffer, int fileOffset) {
120-
final file = vfs.fileData[path] ?? Uint8List(0);
121-
final increasedSize = fileOffset + buffer.length - file.length;
122-
123-
if (increasedSize <= 0) {
124-
// Can write directy
125-
file.setRange(fileOffset, fileOffset + buffer.length, buffer);
126-
} else {
127-
// We need to grow the file first
128-
final newFile = Uint8List(file.length + increasedSize)
129-
..setAll(0, file)
130-
..setAll(fileOffset, buffer);
121+
var file = vfs.fileData[path];
131122

132-
vfs.fileData[path] = newFile;
123+
if (file == null) {
124+
file = DynamicBuffer();
125+
vfs.fileData[path] = file;
133126
}
127+
file.write(buffer, fileOffset);
134128
}
135129
}

0 commit comments

Comments
 (0)