mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-12 22:41:27 +00:00
Implement a mechanism to realloc array buffers; Trap when trying to allocate more than max size; Test allocators in CI
This commit is contained in:
@ -7,15 +7,15 @@
|
||||
* @module std/assembly/allocator/arena
|
||||
*//***/
|
||||
|
||||
import { AL_MASK } from "../internal/allocator";
|
||||
import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
|
||||
|
||||
var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
|
||||
var offset: usize = startOffset;
|
||||
|
||||
@global
|
||||
export function allocate_memory(size: usize): usize {
|
||||
const MAX_SIZE: usize = 1 << 30;
|
||||
if (size && size < MAX_SIZE) {
|
||||
if (size) {
|
||||
if (size > MAX_SIZE_32) unreachable();
|
||||
let ptr = offset;
|
||||
let newPtr = (ptr + size + AL_MASK) & ~AL_MASK;
|
||||
let pagesBefore = current_memory();
|
||||
|
@ -347,9 +347,7 @@ export function allocate_memory(request: usize): usize {
|
||||
* a hard-coded limit on the maximum allocation size because of the way this
|
||||
* allocator works.
|
||||
*/
|
||||
if (request > MAX_ALLOC - HEADER_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
if (request > MAX_ALLOC - HEADER_SIZE) unreachable();
|
||||
|
||||
/*
|
||||
* Initialize our global state if this is the first call to "malloc". At the
|
||||
|
@ -457,7 +457,8 @@ export function allocate_memory(size: usize): usize {
|
||||
|
||||
// search for a suitable block
|
||||
var data: usize = 0;
|
||||
if (size && size <= Block.MAX_SIZE) {
|
||||
if (size) {
|
||||
if (size > Block.MAX_SIZE) unreachable();
|
||||
// 32-bit MAX_SIZE is 1 << 30 and itself aligned, hence the following can't overflow MAX_SIZE
|
||||
size = max<usize>((size + AL_MASK) & ~AL_MASK, Block.MIN_SIZE);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
HEADER_SIZE,
|
||||
MAX_BLENGTH,
|
||||
allocate
|
||||
allocUnsafe
|
||||
} from "./internal/arraybuffer";
|
||||
|
||||
@sealed
|
||||
@ -11,7 +11,7 @@ export class ArrayBuffer {
|
||||
|
||||
constructor(length: i32) {
|
||||
if (<u32>length > <u32>MAX_BLENGTH) throw new RangeError("Invalid array buffer length");
|
||||
var buffer = allocate(length);
|
||||
var buffer = allocUnsafe(length);
|
||||
set_memory(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>length);
|
||||
return buffer;
|
||||
}
|
||||
@ -23,7 +23,7 @@ export class ArrayBuffer {
|
||||
if (end < 0) end = max(len + end, 0);
|
||||
else end = min(end, len);
|
||||
var newLen = max(end - begin, 0);
|
||||
var buffer = allocate(newLen);
|
||||
var buffer = allocUnsafe(newLen);
|
||||
move_memory(changetype<usize>(buffer) + HEADER_SIZE, changetype<usize>(this) + HEADER_SIZE + begin, newLen);
|
||||
return buffer;
|
||||
}
|
||||
|
@ -17,14 +17,44 @@ export function computeSize(byteLength: i32): usize {
|
||||
return <usize>1 << <usize>(<u32>32 - clz<u32>(byteLength + HEADER_SIZE - 1));
|
||||
}
|
||||
|
||||
/** Allocates a raw ArrayBuffer with uninitialized contents. */
|
||||
export function allocate(byteLength: i32): ArrayBuffer {
|
||||
/** Allocates a raw ArrayBuffer. Contents remain uninitialized. */
|
||||
export function allocUnsafe(byteLength: i32): ArrayBuffer {
|
||||
assert(<u32>byteLength <= <u32>MAX_BLENGTH);
|
||||
var buffer = allocate_memory(computeSize(byteLength));
|
||||
store<i32>(buffer, byteLength, offsetof<ArrayBuffer>("byteLength"));
|
||||
return changetype<ArrayBuffer>(buffer);
|
||||
}
|
||||
|
||||
/** Reallocates an ArrayBuffer, resizing it as requested. Tries to modify the buffer in place. */
|
||||
export function reallocUnsafe(buffer: ArrayBuffer, newByteLength: i32): ArrayBuffer {
|
||||
var oldByteLength = buffer.byteLength;
|
||||
if (newByteLength > oldByteLength) {
|
||||
assert(newByteLength <= MAX_BLENGTH);
|
||||
let oldSize = computeSize(oldByteLength);
|
||||
if (<i32>(oldSize - HEADER_SIZE) <= newByteLength) { // fast path: zero out additional space
|
||||
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
|
||||
set_memory(
|
||||
changetype<usize>(buffer) + HEADER_SIZE + oldByteLength,
|
||||
0,
|
||||
<usize>(newByteLength - oldByteLength)
|
||||
);
|
||||
} else { // slow path: copy to new buffer
|
||||
let newBuffer = allocUnsafe(newByteLength);
|
||||
move_memory(
|
||||
changetype<usize>(newBuffer) + HEADER_SIZE,
|
||||
changetype<usize>(buffer) + HEADER_SIZE,
|
||||
<usize>newByteLength
|
||||
);
|
||||
return newBuffer;
|
||||
}
|
||||
} else if (newByteLength < oldByteLength) { // fast path: override size
|
||||
// TBD: worth to copy and release if size is significantly less than before?
|
||||
assert(newByteLength >= 0);
|
||||
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/** Common typed array interface. Not a global object. */
|
||||
// export declare interface ArrayBufferView<T> {
|
||||
// readonly buffer: ArrayBuffer;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
HEADER_SIZE,
|
||||
MAX_BLENGTH,
|
||||
allocate
|
||||
allocUnsafe
|
||||
// ArrayBufferView
|
||||
} from "./arraybuffer";
|
||||
|
||||
@ -15,7 +16,9 @@ export abstract class TypedArray<T> /* implements ArrayBufferView<T> */ {
|
||||
const MAX_LENGTH = <u32>MAX_BLENGTH / sizeof<T>();
|
||||
if (<u32>length > MAX_LENGTH) throw new RangeError("Invalid typed array length");
|
||||
var byteLength = length << alignof<T>();
|
||||
this.buffer = allocate(byteLength);
|
||||
var buffer = allocUnsafe(byteLength);
|
||||
set_memory(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>byteLength);
|
||||
this.buffer = buffer;
|
||||
this.byteOffset = 0;
|
||||
this.byteLength = byteLength;
|
||||
}
|
||||
|
Reference in New Issue
Block a user