diff --git a/src/browser/Factory.zig b/src/browser/Factory.zig
index 91e011ab..69cf0f52 100644
--- a/src/browser/Factory.zig
+++ b/src/browser/Factory.zig
@@ -196,8 +196,8 @@ pub fn blob(self: *Factory, child: anytype) !*@TypeOf(child) {
const blob_ptr = chain.get(0);
blob_ptr.* = .{
._type = unionInit(Blob.Type, chain.get(1)),
- .slice = "",
- .mime = "",
+ ._slice = "",
+ ._mime = "",
};
chain.setLeaf(1, child);
diff --git a/src/browser/tests/blob.html b/src/browser/tests/blob.html
index 693095c1..12cd13f5 100644
--- a/src/browser/tests/blob.html
+++ b/src/browser/tests/blob.html
@@ -1,8 +1,6 @@
-
- Test Document Title
-
-
+
+
-
-
@@ -60,10 +31,11 @@
const parts = ["light ", "panda ", "rocks ", "!"];
const blob = new Blob(parts);
- testing.async(blob.bytes(), result => {
+ testing.async(async() => {
const expected = new Uint8Array([108, 105, 103, 104, 116, 32, 112, 97,
110, 100, 97, 32, 114, 111, 99, 107, 115,
32, 33]);
+ const result = await blob.bytes();
testing.expectEqual(true, result instanceof Uint8Array);
testing.expectEqual(expected, result);
});
@@ -81,7 +53,7 @@
const blob = new Blob(parts, { type: "text/html", endings: "native" });
testing.expectEqual(161, blob.size);
testing.expectEqual("text/html", blob.type);
- testing.async(blob.bytes(), result => {
+ testing.async(async() => {
const expected = new Uint8Array([10, 84, 104, 101, 32, 111, 112, 101, 110,
101, 100, 32, 112, 97, 99, 107, 97, 103,
101, 10, 111, 102, 32, 112, 111, 116, 97,
@@ -100,8 +72,65 @@
10, 10, 116, 111, 32, 115, 111, 108, 118, 101,
32, 116, 104, 101, 32, 10, 99, 114, 105, 109,
101, 46, 10]);
+ const result = await blob.bytes();
testing.expectEqual(true, result instanceof Uint8Array);
testing.expectEqual(expected, result);
});
}
+
+
+
+
diff --git a/src/browser/webapi/Blob.zig b/src/browser/webapi/Blob.zig
index a60f4b42..58106caf 100644
--- a/src/browser/webapi/Blob.zig
+++ b/src/browser/webapi/Blob.zig
@@ -27,14 +27,15 @@ const Page = @import("../Page.zig");
const Blob = @This();
const _prototype_root = true;
+
_type: Type,
/// Immutable slice of blob.
/// Note that another blob may hold a pointer/slice to this,
/// so its better to leave the deallocation of it to arena allocator.
-slice: []const u8,
+_slice: []const u8,
/// MIME attached to blob. Can be an empty string.
-mime: []const u8,
+_mime: []const u8,
pub const Type = union(enum) {
generic,
@@ -66,7 +67,7 @@ pub fn init(
break :blk try page.arena.dupe(u8, t);
};
- const slice = blk: {
+ const data = blk: {
if (maybe_blob_parts) |blob_parts| {
var w: Writer.Allocating = .init(page.arena);
const use_native_endings = std.mem.eql(u8, options.endings, "native");
@@ -80,8 +81,8 @@ pub fn init(
return page._factory.create(Blob{
._type = .generic,
- .slice = slice,
- .mime = mime,
+ ._slice = data,
+ ._mime = mime,
});
}
@@ -147,8 +148,8 @@ fn writeBlobParts(
while (end + vector_len <= part.len) : (end += vector_len) {
const cr: Vec = @splat('\r');
// Load chunk as vectors.
- const slice = part[end..][0..vector_len];
- const chunk: Vec = slice.*;
+ const data = part[end..][0..vector_len];
+ const chunk: Vec = data.*;
// Look for CR.
const match = chunk == cr;
@@ -160,16 +161,16 @@ fn writeBlobParts(
var iter = bitset.iterator(.{});
var relative_start: usize = 0;
while (iter.next()) |index| {
- _ = try writer.writeVec(&.{ slice[relative_start..index], "\n" });
+ _ = try writer.writeVec(&.{ data[relative_start..index], "\n" });
- if (index + 1 != slice.len and slice[index + 1] == '\n') {
+ if (index + 1 != data.len and data[index + 1] == '\n') {
relative_start = index + 2;
} else {
relative_start = index + 1;
}
}
- _ = try writer.writeVec(&.{slice[relative_start..]});
+ _ = try writer.writeVec(&.{data[relative_start..]});
}
}
@@ -204,16 +205,21 @@ fn writeBlobParts(
/// Returns a Promise that resolves with the contents of the blob
/// as binary data contained in an ArrayBuffer.
-//pub fn arrayBuffer(self: *const Blob, page: *Page) !js.Promise {
-// return page.js.resolvePromise(js.ArrayBuffer{ .values = self.slice });
-//}
+pub fn arrayBuffer(self: *const Blob, page: *Page) !js.Promise {
+ return page.js.resolvePromise(js.ArrayBuffer{ .values = self._slice });
+}
-// TODO: Implement `stream`; requires `ReadableStream`.
+const ReadableStream = @import("streams/ReadableStream.zig");
+/// Returns a ReadableStream which upon reading returns the data
+/// contained within the Blob.
+pub fn stream(self: *const Blob, page: *Page) !*ReadableStream {
+ return ReadableStream.initWithData(self._slice, page);
+}
/// Returns a Promise that resolves with a string containing
/// the contents of the blob, interpreted as UTF-8.
pub fn text(self: *const Blob, page: *Page) !js.Promise {
- return page.js.resolvePromise(self.slice);
+ return page.js.resolvePromise(self._slice);
}
/// Extension to Blob; works on Firefox and Safari.
@@ -221,12 +227,12 @@ pub fn text(self: *const Blob, page: *Page) !js.Promise {
/// Returns a Promise that resolves with a Uint8Array containing
/// the contents of the blob as an array of bytes.
pub fn bytes(self: *const Blob, page: *Page) !js.Promise {
- return page.js.resolvePromise(js.TypedArray(u8){ .values = self.slice });
+ return page.js.resolvePromise(js.TypedArray(u8){ .values = self._slice });
}
/// Returns a new Blob object which contains data
/// from a subset of the blob on which it's called.
-pub fn getSlice(
+pub fn slice(
self: *const Blob,
maybe_start: ?i32,
maybe_end: ?i32,
@@ -239,56 +245,56 @@ pub fn getSlice(
break :blk "";
}
- break :blk try page.arena.dupe(u8, content_type);
+ break :blk try page.dupeString(content_type);
}
break :blk "";
};
- const slice = self.slice;
+ const data = self._slice;
if (maybe_start) |_start| {
const start = blk: {
if (_start < 0) {
- break :blk slice.len -| @abs(_start);
+ break :blk data.len -| @abs(_start);
}
- break :blk @min(slice.len, @as(u31, @intCast(_start)));
+ break :blk @min(data.len, @as(u31, @intCast(_start)));
};
const end: usize = blk: {
if (maybe_end) |_end| {
if (_end < 0) {
- break :blk @max(start, slice.len -| @abs(_end));
+ break :blk @max(start, data.len -| @abs(_end));
}
- break :blk @min(slice.len, @max(start, @as(u31, @intCast(_end))));
+ break :blk @min(data.len, @max(start, @as(u31, @intCast(_end))));
}
- break :blk slice.len;
+ break :blk data.len;
};
return page._factory.create(Blob{
._type = .generic,
- .slice = slice[start..end],
- .mime = mime,
+ ._slice = data[start..end],
+ ._mime = mime,
});
}
return page._factory.create(Blob{
._type = .generic,
- .slice = slice,
- .mime = mime,
+ ._slice = data,
+ ._mime = mime,
});
}
/// Returns the size of the Blob in bytes.
pub fn getSize(self: *const Blob) usize {
- return self.slice.len;
+ return self._slice.len;
}
/// Returns the type of Blob; likely a MIME type, yet anything can be given.
pub fn getType(self: *const Blob) []const u8 {
- return self.mime;
+ return self._mime;
}
pub const JsApi = struct {
@@ -303,9 +309,11 @@ pub const JsApi = struct {
pub const constructor = bridge.constructor(Blob.init, .{});
pub const text = bridge.function(Blob.text, .{});
pub const bytes = bridge.function(Blob.bytes, .{});
- pub const slice = bridge.function(Blob.getSlice, .{});
+ pub const slice = bridge.function(Blob.slice, .{});
pub const size = bridge.accessor(Blob.getSize, null, .{});
pub const @"type" = bridge.accessor(Blob.getType, null, .{});
+ pub const stream = bridge.function(Blob.stream, .{});
+ pub const arrayBuffer = bridge.function(Blob.arrayBuffer, .{});
};
const testing = @import("../../testing.zig");