Skip to content
This repository was archived by the owner on Jul 12, 2024. It is now read-only.

Commit 9eb1a99

Browse files
committedApr 19, 2024
Always use 4 bytes to store byte lengths of subsections.
This allows to directly write to a unique target buffer. It will also provide known global offsets to generate source maps without major recomputation hoops. The amount of bytes "lost" should be about 2xN where N is the number of declared functions. That is because we can expect most functions to have a size that should require 2 bytes to store. The lengths of actual full *sections* won't matter as there are only a dozen of them.
1 parent 7465e39 commit 9eb1a99

File tree

1 file changed

+53
-15
lines changed

1 file changed

+53
-15
lines changed
 

‎wasm/src/main/scala/converters/WasmBinaryWriter.scala

+53-15
Original file line numberDiff line numberDiff line change
@@ -458,15 +458,34 @@ object WasmBinaryWriter {
458458
private final val SectionTag = 0x0D
459459

460460
private final class Buffer {
461-
private val buf = new java.io.ByteArrayOutputStream()
461+
private var buf: Array[Byte] = new Array[Byte](1024 * 1024)
462+
private var size: Int = 0
462463

463-
def result(): Array[Byte] = buf.toByteArray()
464+
private def ensureCapacity(capacity: Int): Unit = {
465+
if (buf.length < capacity) {
466+
val newCapacity = Integer.highestOneBit(capacity) << 1
467+
buf = java.util.Arrays.copyOf(buf, newCapacity)
468+
}
469+
}
464470

465-
def byte(b: Byte): Unit =
466-
buf.write(b & 0xFF)
471+
def currentGlobalOffset: Int = size
467472

468-
def rawByteArray(array: Array[Byte]): Unit =
469-
buf.write(array)
473+
def result(): Array[Byte] =
474+
java.util.Arrays.copyOf(buf, size)
475+
476+
def byte(b: Byte): Unit = {
477+
val newSize = size + 1
478+
ensureCapacity(newSize)
479+
buf(size) = b
480+
size = newSize
481+
}
482+
483+
def rawByteArray(array: Array[Byte]): Unit = {
484+
val newSize = size + array.length
485+
ensureCapacity(newSize)
486+
System.arraycopy(array, 0, buf, size, array.length)
487+
size = newSize
488+
}
470489

471490
def boolean(b: Boolean): Unit =
472491
byte(if (b) 1 else 0)
@@ -526,21 +545,40 @@ object WasmBinaryWriter {
526545
}
527546

528547
def byteLengthSubSection(f: Buffer => Unit): Unit = {
529-
val subBuffer = new Buffer()
530-
f(subBuffer)
531-
val subResult = subBuffer.result()
548+
// Reserve 4 bytes at the current offset to store the byteLength later
549+
val byteLengthOffset = size
550+
val startOffset = byteLengthOffset + 4
551+
ensureCapacity(startOffset)
552+
size = startOffset // do not write the 4 bytes for now
553+
554+
f(this)
555+
556+
// Compute byteLength
557+
val endOffset = size
558+
val byteLength = endOffset - startOffset
559+
560+
assert(byteLength < (1 << 28), s"Cannot write a subsection that large: $byteLength")
532561

533-
this.u32(subResult.length)
534-
this.rawByteArray(subResult)
562+
/* Write the byteLength in the reserved slot. Note that we *always* use
563+
* 4 bytes to store the byteLength, even when less bytes are necessary in
564+
* the unsigned LEB encoding. The WebAssembly spec specifically calls out
565+
* this choice as valid. We leverage it to have predictable total offsets
566+
* when write the code section, which is important to efficiently
567+
* generate source maps.
568+
*/
569+
buf(byteLengthOffset) = ((byteLength & 0x7F) | 0x80).toByte
570+
buf(byteLengthOffset + 1) = (((byteLength >>> 7) & 0x7F) | 0x80).toByte
571+
buf(byteLengthOffset + 2) = (((byteLength >>> 14) & 0x7F) | 0x80).toByte
572+
buf(byteLengthOffset + 3) = ((byteLength >>> 21) & 0x7F).toByte
535573
}
536574

537575
@tailrec
538576
private def unsignedLEB128(value: Long): Unit = {
539577
val next = value >>> 7
540578
if (next == 0) {
541-
buf.write(value.toInt)
579+
byte(value.toByte)
542580
} else {
543-
buf.write((value.toInt & 0x7F) | 0x80)
581+
byte(((value.toInt & 0x7F) | 0x80).toByte)
544582
unsignedLEB128(next)
545583
}
546584
}
@@ -550,9 +588,9 @@ object WasmBinaryWriter {
550588
val chunk = value.toInt & 0x7F
551589
val next = value >> 7
552590
if (next == (if ((chunk & 0x40) != 0) -1 else 0)) {
553-
buf.write(chunk)
591+
byte(chunk.toByte)
554592
} else {
555-
buf.write(chunk | 0x80)
593+
byte((chunk | 0x80).toByte)
556594
signedLEB128(next)
557595
}
558596
}

0 commit comments

Comments
 (0)
This repository has been archived.