ferrijs_std/buffer/
mod.rs1use crate::utils::{
6 module::{export_default, ModuleInfo},
7 object::define_subclass,
8 primordials::{BasePrimordials, Primordial},
9};
10use rquickjs::{
11 function::{Args, Constructor, Rest},
12 module::{Declarations, Exports, ModuleDef},
13 Ctx, Function, IntoJs, Object, Result, Value,
14};
15
16pub use self::array_buffer_view::*;
17pub use self::blob::*;
18pub use self::class::*;
19pub use self::file::*;
20
21mod array_buffer_view;
22mod blob;
23mod class;
24mod file;
25
26pub struct BufferModule;
27
28impl ModuleDef for BufferModule {
29 fn declare(declare: &Declarations) -> Result<()> {
30 declare.declare(stringify!(Buffer))?;
31 declare.declare("atob")?;
32 declare.declare("btoa")?;
33 declare.declare("constants")?;
34 declare.declare("default")?;
35 Ok(())
36 }
37
38 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> Result<()> {
39 let globals = ctx.globals();
40 let buf: Constructor = globals.get(stringify!(Buffer))?;
41
42 let constants = Object::new(ctx.clone())?;
43 constants.set("MAX_LENGTH", u32::MAX)?; constants.set("MAX_STRING_LENGTH", (1 << 30) - 1)?; let atob: Function = ctx.globals().get("atob")?;
47 let btoa: Function = ctx.globals().get("btoa")?;
48
49 export_default(ctx, exports, |default| {
50 default.set(stringify!(Buffer), buf)?;
51 default.set("atob", atob.into_js(ctx)?)?;
52 default.set("btoa", btoa.into_js(ctx)?)?;
53 default.set("constants", constants)?;
54 Ok(())
55 })?;
56
57 Ok(())
58 }
59}
60
61impl From<BufferModule> for ModuleInfo<BufferModule> {
62 fn from(val: BufferModule) -> Self {
63 ModuleInfo {
64 name: "buffer",
65 module: val,
66 }
67 }
68}
69
70pub fn init<'js>(ctx: &Ctx<'js>) -> Result<()> {
71 BasePrimordials::init(ctx)?;
72
73 let uint8array = BasePrimordials::get(ctx)?.constructor_uint8array.clone();
76 let buffer_ctor = define_subclass(
77 ctx,
78 stringify!(Buffer),
79 &uint8array,
80 |ctx: Ctx<'js>, args: Rest<Value<'js>>| {
81 let uint8array = &BasePrimordials::get(&ctx)?.constructor_uint8array;
82 let mut ctor_args = Args::new(ctx.clone(), args.0.len());
83 ctor_args.push_args(args.0)?;
84 ctor_args.construct::<Value>(uint8array)
85 },
86 )?;
87 let buffer: Object = buffer_ctor.into_value().into_object().unwrap();
88 set_prototype(ctx, buffer)?;
89
90 BufferPrimordials::init(ctx)?;
91
92 add_missing_prototype_methods(ctx)?;
97
98 rquickjs::Class::<Blob>::define(&ctx.globals())?;
100
101 rquickjs::Class::<File>::define(&ctx.globals())?;
103 chain_file_to_blob(ctx)?;
108
109 let _ = BufferPrimordials::get(ctx)?;
111
112 Ok(())
113}
114
115fn add_missing_prototype_methods<'js>(ctx: &Ctx<'js>) -> Result<()> {
117 let buffer: Constructor<'js> = ctx.globals().get(stringify!(Buffer))?;
118 let prototype: Object<'js> = buffer.get(rquickjs::atom::PredefinedAtom::Prototype)?;
119
120 prototype.set(
121 "equals",
122 Function::new(ctx.clone(), |this: rquickjs::function::This<Object<'js>>, other: Object<'js>| -> Result<bool> {
123 let (a, b) = (bytes_of(&this.0)?, bytes_of(&other)?);
124 Ok(a == b)
125 })?,
126 )?;
127
128 prototype.set(
129 "toJSON",
130 Function::new(ctx.clone(), |ctx: Ctx<'js>, this: rquickjs::function::This<Object<'js>>| -> Result<Object<'js>> {
131 let json = Object::new(ctx.clone())?;
132 json.set("type", "Buffer")?;
133 json.set("data", bytes_of(&this.0)?)?;
134 Ok(json)
135 })?,
136 )?;
137
138 Ok(())
139}
140
141fn bytes_of<'js>(object: &Object<'js>) -> Result<Vec<u8>> {
143 match crate::utils::bytes::ObjectBytes::from_array_buffer(object)? {
144 Some(bytes) => Ok(bytes.as_bytes(object.ctx())?.to_vec()),
145 None => Ok(Vec::new()),
146 }
147}
148
149fn chain_file_to_blob<'js>(ctx: &Ctx<'js>) -> Result<()> {
152 let blob_proto = rquickjs::Class::<Blob<'js>>::prototype(ctx)?;
153 let file_proto = rquickjs::Class::<File<'js>>::prototype(ctx)?;
154 if let (Some(blob_proto), Some(file_proto)) = (blob_proto, file_proto) {
155 file_proto.set_prototype(Some(&blob_proto))?;
156 }
157 Ok(())
158}