Skip to content

Commit 6b4f3e3

Browse files
committed
Add a GrowingByteBuffer for VFS implementations.
1 parent 0f0bddf commit 6b4f3e3

File tree

3 files changed

+105
-24
lines changed

3 files changed

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

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/growing_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 = GrowingByteBuffer();
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] ?? GrowingByteBuffer();
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 'growing_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, GrowingByteBuffer?> 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] = GrowingByteBuffer();
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] = GrowingByteBuffer();
108+
// TODO: grow?
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 = GrowingByteBuffer();
125+
vfs.fileData[path] = file;
133126
}
127+
file.write(buffer, fileOffset);
134128
}
135129
}

0 commit comments

Comments
 (0)