Skip to main content

ckb_x64_simulator/
lib.rs

1pub mod constants;
2
3pub mod spawn;
4pub use spawn::*;
5
6mod global_data;
7mod simulator_context;
8mod utils;
9
10use global_data::GlobalData;
11use simulator_context::SimContext;
12
13#[macro_use]
14extern crate lazy_static;
15
16use ckb_mock_tx_types::{MockTransaction, ReprMockTransaction};
17use ckb_types::{
18    bytes::Bytes,
19    core::{Capacity, HeaderView, cell::CellMetaBuilder},
20    packed::{self, Byte32, CellInput, CellOutput, Script},
21    prelude::*,
22};
23use constants::{
24    CELL_FIELD_CAPACITY, CELL_FIELD_DATA_HASH, CELL_FIELD_LOCK, CELL_FIELD_LOCK_HASH, CELL_FIELD_OCCUPIED_CAPACITY,
25    CELL_FIELD_TYPE, CELL_FIELD_TYPE_HASH, CKB_INDEX_OUT_OF_BOUND, CKB_ITEM_MISSING, CKB_SUCCESS,
26    HEADER_FIELD_EPOCH_LENGTH, HEADER_FIELD_EPOCH_NUMBER, HEADER_FIELD_EPOCH_START_BLOCK_NUMBER, INPUT_FIELD_OUT_POINT,
27    INPUT_FIELD_SINCE, SOURCE_CELL_DEP, SOURCE_GROUP_CELL_DEP, SOURCE_GROUP_HEADER_DEP, SOURCE_GROUP_INPUT,
28    SOURCE_GROUP_OUTPUT, SOURCE_HEADER_DEP, SOURCE_INPUT, SOURCE_OUTPUT,
29};
30use serde_derive::{Deserialize, Serialize};
31use std::collections::HashMap;
32use std::ffi::CString;
33use std::os::raw::{c_char, c_int, c_void};
34
35#[derive(Clone, Serialize, Deserialize)]
36pub enum RunningType {
37    Executable,
38    DynamicLib,
39}
40
41#[derive(Clone, Serialize, Deserialize)]
42pub struct RunningSetup {
43    pub is_lock_script: bool,
44    pub is_output: bool,
45    pub script_index: u64,
46    pub vm_version: i32,
47    pub native_binaries: HashMap<String, String>,
48    pub run_type: Option<RunningType>,
49}
50
51lazy_static! {
52    static ref TRANSACTION: MockTransaction = {
53        let tx_filename = std::env::var("CKB_TX_FILE").expect("environment variable");
54        let tx_content = std::fs::read_to_string(tx_filename).expect("read tx file");
55        let repr_mock_tx: ReprMockTransaction = serde_json::from_str(&tx_content).expect("parse tx file");
56        let mock_tx: MockTransaction = repr_mock_tx.into();
57        mock_tx
58    };
59    static ref SETUP: RunningSetup = {
60        let setup_filename = std::env::var("CKB_RUNNING_SETUP").expect("environment variable");
61        let setup_content = std::fs::read_to_string(setup_filename).expect("read setup file");
62        serde_json::from_str(&setup_content).expect("parse setup file")
63    };
64}
65
66fn assert_vm_version() {
67    if SETUP.vm_version == 0 {
68        panic!("Currently running setup vm_version({}) not support this syscall", SETUP.vm_version);
69    }
70}
71
72#[unsafe(no_mangle)]
73pub extern "C" fn ckb_exit(code: i8) -> i32 {
74    std::process::exit(code.into());
75}
76
77#[unsafe(no_mangle)]
78pub extern "C" fn ckb_vm_version() -> c_int {
79    assert_vm_version();
80    SETUP.vm_version
81}
82
83#[unsafe(no_mangle)]
84pub extern "C" fn ckb_current_cycles() -> u64 {
85    assert_vm_version();
86    // NOTE: return a fake number since this value is meaningless in simulator
87    333
88}
89
90/// The binary key string is 0x{code_hash + hash_type + offset.to_be_bytes() + length.to_be_bytes()}
91#[unsafe(no_mangle)]
92pub extern "C" fn ckb_exec_cell(
93    code_hash: *const u8,
94    hash_type: u8,
95    offset: u32,
96    length: u32,
97    argc: i32,
98    argv: *const *const u8,
99) -> c_int {
100    assert_vm_version();
101
102    let sim_path = utils::get_simulator_path(utils::to_array(code_hash, 32), hash_type, offset, length);
103    let sim_path = sim_path.expect("cannot locate native binary for ckb_exec syscall!");
104
105    match SETUP.run_type.as_ref().unwrap_or(&RunningType::Executable) {
106        RunningType::Executable => {
107            let filename_cstring = CString::new(sim_path.as_bytes().to_vec()).unwrap();
108            unsafe {
109                let args = argv as *const *const i8;
110                libc::execvp(filename_cstring.as_ptr(), args)
111            }
112        }
113        RunningType::DynamicLib => {
114            use utils::CkbNativeSimulator;
115
116            let tx_ctx_id = GlobalData::locked().set_tx(simulator_context::SimContext::default());
117            SimContext::update_ctx_id(tx_ctx_id.clone(), None);
118
119            let sim = CkbNativeSimulator::new_by_hash(code_hash, hash_type, offset, length);
120            let args = utils::to_vec_args(argc, argv as *const *const i8);
121
122            let join_handle = {
123                let mut global_data = GlobalData::locked();
124                let sim_ctx = global_data.get_tx_mut(&tx_ctx_id);
125                let child_pid: utils::ProcID = sim_ctx.start_process(&[], move |sim_id, pid| {
126                    sim.update_script_info(sim_id, pid);
127                    sim.ckb_std_main(args)
128                });
129                sim_ctx.exit(&child_pid).unwrap()
130            };
131            join_handle.join().expect("exec dylib") as c_int
132        }
133    }
134}
135
136#[unsafe(no_mangle)]
137pub extern "C" fn ckb_load_tx_hash(ptr: *mut c_void, len: *mut u64, offset: u64) -> c_int {
138    let view = TRANSACTION.tx.clone().into_view();
139    store_data(ptr, len, offset, view.hash().as_slice());
140    CKB_SUCCESS
141}
142
143#[unsafe(no_mangle)]
144pub extern "C" fn ckb_load_transaction(ptr: *mut c_void, len: *mut u64, offset: u64) -> c_int {
145    store_data(ptr, len, offset, TRANSACTION.tx.as_slice());
146    CKB_SUCCESS
147}
148
149#[unsafe(no_mangle)]
150pub extern "C" fn ckb_load_script_hash(ptr: *mut c_void, len: *mut u64, offset: u64) -> c_int {
151    let hash = fetch_current_script().calc_script_hash();
152    store_data(ptr, len, offset, hash.as_slice());
153    CKB_SUCCESS
154}
155
156#[unsafe(no_mangle)]
157pub extern "C" fn ckb_load_script(ptr: *mut c_void, len: *mut u64, offset: u64) -> c_int {
158    store_data(ptr, len, offset, fetch_current_script().as_slice());
159    CKB_SUCCESS
160}
161
162#[unsafe(no_mangle)]
163pub extern "C" fn ckb_debug(s: *const c_char) {
164    let message = utils::to_c_str(s).to_str().expect("UTF8 error!");
165    // println!("Debug message: {}", message);
166    println!("[contract debug] {}", message);
167}
168
169#[unsafe(no_mangle)]
170pub extern "C" fn ckb_load_cell(ptr: *mut c_void, len: *mut u64, offset: u64, index: u64, source: u64) -> c_int {
171    let (cell, _) = match fetch_cell(index, source) {
172        Ok(cell) => cell,
173        Err(code) => return code,
174    };
175    store_data(ptr, len, offset, cell.as_slice());
176    CKB_SUCCESS
177}
178
179#[unsafe(no_mangle)]
180pub extern "C" fn ckb_load_input(ptr: *mut c_void, len: *mut u64, offset: u64, index: u64, source: u64) -> c_int {
181    let input = match fetch_input(index, source) {
182        Ok(input) => input,
183        Err(code) => return code,
184    };
185    store_data(ptr, len, offset, input.as_slice());
186    CKB_SUCCESS
187}
188
189#[unsafe(no_mangle)]
190pub extern "C" fn ckb_load_header(ptr: *mut c_void, len: *mut u64, offset: u64, index: u64, source: u64) -> c_int {
191    let header = match fetch_header(index, source) {
192        Ok(input) => input,
193        Err(code) => return code,
194    };
195    store_data(ptr, len, offset, header.data().as_slice());
196    CKB_SUCCESS
197}
198
199#[unsafe(no_mangle)]
200pub extern "C" fn ckb_load_witness(ptr: *mut c_void, len: *mut u64, offset: u64, index: u64, source: u64) -> c_int {
201    let witness = match fetch_witness(index, source) {
202        Some(witness) => witness,
203        None => return CKB_INDEX_OUT_OF_BOUND,
204    };
205    store_data(ptr, len, offset, &witness.raw_data());
206    CKB_SUCCESS
207}
208
209#[unsafe(no_mangle)]
210pub extern "C" fn ckb_load_cell_by_field(
211    ptr: *mut c_void,
212    len: *mut u64,
213    offset: u64,
214    index: u64,
215    source: u64,
216    field: u64,
217) -> c_int {
218    let (cell, cell_data) = match fetch_cell(index, source) {
219        Ok(cell) => cell,
220        Err(code) => return code,
221    };
222    let cell_meta = CellMetaBuilder::from_cell_output(cell.clone(), cell_data.clone()).build();
223    match field {
224        CELL_FIELD_CAPACITY => {
225            let capacity: Capacity = cell.capacity().unpack();
226            let data = capacity.as_u64().to_le_bytes();
227            store_data(ptr, len, offset, &data[..]);
228        }
229        CELL_FIELD_DATA_HASH => {
230            let hash = CellOutput::calc_data_hash(&cell_data);
231            store_data(ptr, len, offset, hash.as_slice());
232        }
233        CELL_FIELD_OCCUPIED_CAPACITY => {
234            let data = cell_meta.occupied_capacity().expect("capacity error").as_u64().to_le_bytes();
235            store_data(ptr, len, offset, &data[..]);
236        }
237        CELL_FIELD_LOCK => {
238            let lock = cell.lock();
239            store_data(ptr, len, offset, lock.as_slice());
240        }
241        CELL_FIELD_LOCK_HASH => {
242            let hash = cell.calc_lock_hash();
243            store_data(ptr, len, offset, &hash.as_bytes());
244        }
245        CELL_FIELD_TYPE => match cell.type_().to_opt() {
246            Some(type_) => {
247                store_data(ptr, len, offset, type_.as_slice());
248            }
249            None => {
250                return CKB_ITEM_MISSING;
251            }
252        },
253        CELL_FIELD_TYPE_HASH => match cell.type_().to_opt() {
254            Some(type_) => {
255                let hash = type_.calc_script_hash();
256                store_data(ptr, len, offset, &hash.as_bytes());
257            }
258            None => {
259                return CKB_ITEM_MISSING;
260            }
261        },
262        _ => panic!("Invalid field: {}", field),
263    };
264    CKB_SUCCESS
265}
266
267#[unsafe(no_mangle)]
268pub extern "C" fn ckb_load_header_by_field(
269    ptr: *mut c_void,
270    len: *mut u64,
271    offset: u64,
272    index: u64,
273    source: u64,
274    field: u64,
275) -> c_int {
276    let header = match fetch_header(index, source) {
277        Ok(input) => input,
278        Err(code) => return code,
279    };
280    let epoch = header.epoch();
281    let value = match field {
282        HEADER_FIELD_EPOCH_NUMBER => epoch.number(),
283        HEADER_FIELD_EPOCH_START_BLOCK_NUMBER => header.number().checked_sub(epoch.index()).expect("Overflow!"),
284        HEADER_FIELD_EPOCH_LENGTH => epoch.length(),
285        _ => panic!("Invalid field: {}", field),
286    };
287    let data = value.to_le_bytes();
288    store_data(ptr, len, offset, &data[..]);
289    CKB_SUCCESS
290}
291
292#[unsafe(no_mangle)]
293pub extern "C" fn ckb_load_input_by_field(
294    ptr: *mut c_void,
295    len: *mut u64,
296    offset: u64,
297    index: u64,
298    source: u64,
299    field: u64,
300) -> c_int {
301    let input = match fetch_input(index, source) {
302        Ok(input) => input,
303        Err(code) => return code,
304    };
305    match field {
306        INPUT_FIELD_OUT_POINT => {
307            store_data(ptr, len, offset, input.previous_output().as_slice());
308        }
309        INPUT_FIELD_SINCE => {
310            let since: u64 = input.since().unpack();
311            let data = since.to_le_bytes();
312            store_data(ptr, len, offset, &data[..]);
313        }
314        _ => panic!("Invalid field: {}", field),
315    };
316    CKB_SUCCESS
317}
318
319#[unsafe(no_mangle)]
320pub extern "C" fn ckb_load_cell_data(ptr: *mut c_void, len: *mut u64, offset: u64, index: u64, source: u64) -> c_int {
321    let (_, cell_data) = match fetch_cell(index, source) {
322        Ok(cell) => cell,
323        Err(code) => return code,
324    };
325    store_data(ptr, len, offset, &cell_data);
326    CKB_SUCCESS
327}
328
329unsafe extern "C" {
330    unsafe fn simulator_internal_dlopen2(
331        native_library_path: *const u8,
332        code: *const u8,
333        length: u64,
334        aligned_addr: *mut u8,
335        aligned_size: u64,
336        handle: *mut *mut c_void,
337        consumed_size: *mut u64,
338    ) -> c_int;
339}
340
341// TO fix clippy error: clippy::not_unsafe_ptr_arg_deref
342fn rs_simulator_internal_dlopen2(
343    native_library_path: *const u8,
344    code: *const u8,
345    length: u64,
346    aligned_addr: *mut u8,
347    aligned_size: u64,
348    handle: *mut *mut c_void,
349    consumed_size: *mut u64,
350) -> c_int {
351    unsafe {
352        simulator_internal_dlopen2(native_library_path, code, length, aligned_addr, aligned_size, handle, consumed_size)
353    }
354}
355
356#[unsafe(no_mangle)]
357pub extern "C" fn ckb_dlopen2(
358    dep_cell_hash: *const u8,
359    hash_type: u8,
360    aligned_addr: *mut u8,
361    aligned_size: u64,
362    handle: *mut *mut c_void,
363    consumed_size: *mut u64,
364) -> c_int {
365    let dep_cell_hash = utils::to_array(dep_cell_hash, 32);
366    let mut buffer = vec![];
367    buffer.extend_from_slice(dep_cell_hash);
368    buffer.push(hash_type);
369    let key = format!("0x{}", faster_hex::hex_string(&buffer));
370    let filename = SETUP.native_binaries.get(&key).expect("cannot locate native binary!");
371    let cell_dep = TRANSACTION
372        .mock_info
373        .cell_deps
374        .iter()
375        .find(|cell_dep| {
376            if hash_type == 1 {
377                cell_dep
378                    .output
379                    .type_()
380                    .to_opt()
381                    .map(|t| t.calc_script_hash().as_slice() == dep_cell_hash)
382                    .unwrap_or(false)
383            } else {
384                CellOutput::calc_data_hash(&cell_dep.data).as_slice() == dep_cell_hash
385            }
386        })
387        .expect("cannot locate cell dep");
388    let cell_data = cell_dep.data.as_ref();
389    rs_simulator_internal_dlopen2(
390        filename.as_str().as_ptr(),
391        cell_data.as_ptr(),
392        cell_data.len() as u64,
393        aligned_addr,
394        aligned_size,
395        handle,
396        consumed_size,
397    )
398}
399
400#[unsafe(no_mangle)]
401pub extern "C" fn set_script_info(ptr: *const std::ffi::c_void, tx_ctx_id: u64, proc_ctx_id: u64) {
402    if ptr.is_null() && tx_ctx_id == 0 && proc_ctx_id == 0 {
403        GlobalData::clean();
404    } else {
405        GlobalData::set_ptr(ptr);
406        SimContext::update_ctx_id(tx_ctx_id.into(), Some(proc_ctx_id.into()));
407    }
408}
409
410fn fetch_cell(index: u64, source: u64) -> Result<(CellOutput, Bytes), c_int> {
411    match source {
412        SOURCE_INPUT => TRANSACTION
413            .mock_info
414            .inputs
415            .get(index as usize)
416            .ok_or(CKB_INDEX_OUT_OF_BOUND)
417            .map(|input| (input.output.clone(), input.data.clone())),
418        SOURCE_OUTPUT => {
419            TRANSACTION.tx.raw().outputs().get(index as usize).ok_or(CKB_INDEX_OUT_OF_BOUND).map(|output| {
420                (output, TRANSACTION.tx.raw().outputs_data().get(index as usize).expect("cell data mismatch").unpack())
421            })
422        }
423        SOURCE_CELL_DEP => TRANSACTION
424            .mock_info
425            .cell_deps
426            .get(index as usize)
427            .ok_or(CKB_INDEX_OUT_OF_BOUND)
428            .map(|cell_dep| (cell_dep.output.clone(), cell_dep.data.clone())),
429        SOURCE_HEADER_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
430        SOURCE_GROUP_INPUT => {
431            let (indices, _) = fetch_group_indices();
432            indices.get(index as usize).ok_or(CKB_INDEX_OUT_OF_BOUND).and_then(|actual_index| {
433                TRANSACTION
434                    .mock_info
435                    .inputs
436                    .get(*actual_index)
437                    .ok_or(CKB_INDEX_OUT_OF_BOUND)
438                    .map(|input| (input.output.clone(), input.data.clone()))
439            })
440        }
441        SOURCE_GROUP_OUTPUT => {
442            let (_, indices) = fetch_group_indices();
443            indices.get(index as usize).ok_or(CKB_INDEX_OUT_OF_BOUND).and_then(|actual_index| {
444                TRANSACTION.tx.raw().outputs().get(*actual_index).ok_or(CKB_INDEX_OUT_OF_BOUND).map(|output| {
445                    (
446                        output,
447                        TRANSACTION.tx.raw().outputs_data().get(*actual_index).expect("cell data mismatch").unpack(),
448                    )
449                })
450            })
451        }
452        SOURCE_GROUP_CELL_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
453        SOURCE_GROUP_HEADER_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
454        _ => panic!("Invalid source: {}", source),
455    }
456}
457
458fn fetch_input(index: u64, source: u64) -> Result<CellInput, c_int> {
459    match source {
460        SOURCE_INPUT => TRANSACTION.tx.raw().inputs().get(index as usize).ok_or(CKB_INDEX_OUT_OF_BOUND),
461        SOURCE_OUTPUT => Err(CKB_INDEX_OUT_OF_BOUND),
462        SOURCE_CELL_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
463        SOURCE_HEADER_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
464        SOURCE_GROUP_INPUT => {
465            let (indices, _) = fetch_group_indices();
466            indices
467                .get(index as usize)
468                .ok_or(CKB_INDEX_OUT_OF_BOUND)
469                .and_then(|actual_index| TRANSACTION.tx.raw().inputs().get(*actual_index).ok_or(CKB_INDEX_OUT_OF_BOUND))
470        }
471        SOURCE_GROUP_OUTPUT => Err(CKB_INDEX_OUT_OF_BOUND),
472        SOURCE_GROUP_CELL_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
473        SOURCE_GROUP_HEADER_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
474        _ => panic!("Invalid source: {}", source),
475    }
476}
477
478fn find_header(hash: Byte32) -> Option<HeaderView> {
479    TRANSACTION.mock_info.header_deps.iter().find(|header| header.hash() == hash).cloned()
480}
481
482fn fetch_header(index: u64, source: u64) -> Result<HeaderView, c_int> {
483    match source {
484        SOURCE_INPUT => TRANSACTION
485            .mock_info
486            .inputs
487            .get(index as usize)
488            .and_then(|input| input.header.as_ref().cloned())
489            .ok_or(CKB_INDEX_OUT_OF_BOUND)
490            .and_then(|header_hash| find_header(header_hash).ok_or(CKB_ITEM_MISSING)),
491        SOURCE_OUTPUT => Err(CKB_INDEX_OUT_OF_BOUND),
492        SOURCE_CELL_DEP => TRANSACTION
493            .mock_info
494            .cell_deps
495            .get(index as usize)
496            .and_then(|cell_dep| cell_dep.header.as_ref().cloned())
497            .ok_or(CKB_INDEX_OUT_OF_BOUND)
498            .and_then(|header_hash| find_header(header_hash).ok_or(CKB_ITEM_MISSING)),
499        SOURCE_HEADER_DEP => {
500            TRANSACTION.mock_info.header_deps.get(index as usize).cloned().ok_or(CKB_INDEX_OUT_OF_BOUND)
501        }
502        SOURCE_GROUP_INPUT => {
503            let (indices, _) = fetch_group_indices();
504            indices.get(index as usize).ok_or(CKB_INDEX_OUT_OF_BOUND).and_then(|actual_index| {
505                TRANSACTION
506                    .mock_info
507                    .inputs
508                    .get(*actual_index)
509                    .and_then(|input| input.header.as_ref().cloned())
510                    .ok_or(CKB_INDEX_OUT_OF_BOUND)
511                    .and_then(|header_hash| find_header(header_hash).ok_or(CKB_ITEM_MISSING))
512            })
513        }
514        SOURCE_GROUP_OUTPUT => Err(CKB_INDEX_OUT_OF_BOUND),
515        SOURCE_GROUP_CELL_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
516        SOURCE_GROUP_HEADER_DEP => Err(CKB_INDEX_OUT_OF_BOUND),
517        _ => panic!("Invalid source: {}", source),
518    }
519}
520
521fn fetch_witness(index: u64, source: u64) -> Option<packed::Bytes> {
522    match source {
523        SOURCE_INPUT => TRANSACTION.tx.witnesses().get(index as usize),
524        SOURCE_OUTPUT => TRANSACTION.tx.witnesses().get(index as usize),
525        SOURCE_GROUP_INPUT => {
526            let (indices, _) = fetch_group_indices();
527            indices.get(index as usize).and_then(|actual_index| TRANSACTION.tx.witnesses().get(*actual_index))
528        }
529        SOURCE_GROUP_OUTPUT => {
530            let (_, indices) = fetch_group_indices();
531            indices.get(index as usize).and_then(|actual_index| TRANSACTION.tx.witnesses().get(*actual_index))
532        }
533        SOURCE_CELL_DEP => None,
534        SOURCE_HEADER_DEP => None,
535        SOURCE_GROUP_CELL_DEP => None,
536        SOURCE_GROUP_HEADER_DEP => None,
537        _ => panic!("Invalid source: {}", source),
538    }
539}
540
541fn fetch_group_indices() -> (Vec<usize>, Vec<usize>) {
542    let mut input_indices: Vec<usize> = vec![];
543    let mut output_indices: Vec<usize> = vec![];
544    let current_script = fetch_current_script();
545
546    for (i, input) in TRANSACTION.mock_info.inputs.iter().enumerate() {
547        if SETUP.is_lock_script {
548            if input.output.lock() == current_script {
549                input_indices.push(i);
550            }
551        } else if let Some(t) = input.output.type_().to_opt() {
552            if t == current_script {
553                input_indices.push(i);
554            }
555        }
556    }
557    for (i, output) in TRANSACTION.tx.raw().outputs().into_iter().enumerate() {
558        if let Some(t) = output.type_().to_opt() {
559            if t == current_script {
560                output_indices.push(i);
561            }
562        }
563    }
564    (input_indices, output_indices)
565}
566
567fn fetch_current_script() -> Script {
568    let cell = if SETUP.is_output {
569        TRANSACTION.tx.raw().outputs().get(SETUP.script_index as usize).expect("running script index out of bound!")
570    } else {
571        TRANSACTION
572            .mock_info
573            .inputs
574            .get(SETUP.script_index as usize)
575            .expect("running script index out of bound!")
576            .output
577            .clone()
578    };
579    if SETUP.is_lock_script { cell.lock() } else { cell.type_().to_opt().unwrap() }
580}
581
582fn store_data(ptr: *mut c_void, len: *mut u64, offset: u64, data: &[u8]) {
583    let size_ptr = unsafe { len.as_mut().expect("casting pointer") };
584    let size = *size_ptr;
585    let buffer = unsafe { std::slice::from_raw_parts_mut(ptr as *mut u8, size as usize) };
586    let data_len = data.len() as u64;
587    let offset = std::cmp::min(data_len, offset);
588    let full_size = data_len - offset;
589    let real_size = std::cmp::min(size, full_size);
590    *size_ptr = full_size;
591    buffer[..real_size as usize].copy_from_slice(&data[offset as usize..(offset + real_size) as usize]);
592}