github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/wasm/memory_test.go (about) 1 package wasm 2 3 import ( 4 "math" 5 "reflect" 6 "strings" 7 "testing" 8 "unsafe" 9 10 "github.com/bananabytelabs/wazero/api" 11 "github.com/bananabytelabs/wazero/internal/testing/require" 12 ) 13 14 func TestMemoryPageConsts(t *testing.T) { 15 require.Equal(t, MemoryPageSize, uint32(1)<<MemoryPageSizeInBits) 16 require.Equal(t, MemoryPageSize, uint32(1<<16)) 17 require.Equal(t, MemoryLimitPages, uint32(1<<16)) 18 } 19 20 func TestMemoryPagesToBytesNum(t *testing.T) { 21 for _, numPage := range []uint32{0, 1, 5, 10} { 22 require.Equal(t, uint64(numPage*MemoryPageSize), MemoryPagesToBytesNum(numPage)) 23 } 24 } 25 26 func TestMemoryBytesNumToPages(t *testing.T) { 27 for _, numbytes := range []uint32{0, MemoryPageSize * 1, MemoryPageSize * 10} { 28 require.Equal(t, numbytes/MemoryPageSize, memoryBytesNumToPages(uint64(numbytes))) 29 } 30 } 31 32 func TestMemoryInstance_Grow_Size(t *testing.T) { 33 tests := []struct { 34 name string 35 capEqualsMax bool 36 }{ 37 {name: ""}, 38 {name: "capEqualsMax", capEqualsMax: true}, 39 } 40 41 for _, tt := range tests { 42 tc := tt 43 44 t.Run(tc.name, func(t *testing.T) { 45 max := uint32(10) 46 maxBytes := MemoryPagesToBytesNum(max) 47 var m *MemoryInstance 48 if tc.capEqualsMax { 49 m = &MemoryInstance{Cap: max, Max: max, Buffer: make([]byte, 0, maxBytes)} 50 } else { 51 m = &MemoryInstance{Max: max, Buffer: make([]byte, 0)} 52 } 53 54 res, ok := m.Grow(5) 55 require.True(t, ok) 56 require.Equal(t, uint32(0), res) 57 require.Equal(t, uint32(5), m.PageSize()) 58 59 // Zero page grow is well-defined, should return the current page correctly. 60 res, ok = m.Grow(0) 61 require.True(t, ok) 62 require.Equal(t, uint32(5), res) 63 require.Equal(t, uint32(5), m.PageSize()) 64 65 res, ok = m.Grow(4) 66 require.True(t, ok) 67 require.Equal(t, uint32(5), res) 68 require.Equal(t, uint32(9), m.PageSize()) 69 70 // At this point, the page size equal 9, 71 // so trying to grow two pages should result in failure. 72 _, ok = m.Grow(2) 73 require.False(t, ok) 74 require.Equal(t, uint32(9), m.PageSize()) 75 76 // But growing one page is still permitted. 77 res, ok = m.Grow(1) 78 require.True(t, ok) 79 require.Equal(t, uint32(9), res) 80 81 // Ensure that the current page size equals the max. 82 require.Equal(t, max, m.PageSize()) 83 84 if tc.capEqualsMax { // Ensure the capacity isn't more than max. 85 require.Equal(t, maxBytes, uint64(cap(m.Buffer))) 86 } else { // Slice doubles, so it should have a higher capacity than max. 87 require.True(t, maxBytes < uint64(cap(m.Buffer))) 88 } 89 }) 90 } 91 } 92 93 func TestMemoryInstance_ReadByte(t *testing.T) { 94 mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 0, 0, 0, 16}, Min: 1} 95 v, ok := mem.ReadByte(7) 96 require.True(t, ok) 97 require.Equal(t, byte(16), v) 98 99 _, ok = mem.ReadByte(8) 100 require.False(t, ok) 101 102 _, ok = mem.ReadByte(9) 103 require.False(t, ok) 104 } 105 106 func TestPagesToUnitOfBytes(t *testing.T) { 107 tests := []struct { 108 name string 109 pages uint32 110 expected string 111 }{ 112 { 113 name: "zero", 114 pages: 0, 115 expected: "0 Ki", 116 }, 117 { 118 name: "one", 119 pages: 1, 120 expected: "64 Ki", 121 }, 122 { 123 name: "megs", 124 pages: 100, 125 expected: "6 Mi", 126 }, 127 { 128 name: "max memory", 129 pages: MemoryLimitPages, 130 expected: "4 Gi", 131 }, 132 { 133 name: "max uint32", 134 pages: math.MaxUint32, 135 expected: "3 Ti", 136 }, 137 } 138 139 for _, tt := range tests { 140 tc := tt 141 142 t.Run(tc.name, func(t *testing.T) { 143 require.Equal(t, tc.expected, PagesToUnitOfBytes(tc.pages)) 144 }) 145 } 146 } 147 148 func TestMemoryInstance_HasSize(t *testing.T) { 149 memory := &MemoryInstance{Buffer: make([]byte, MemoryPageSize)} 150 151 tests := []struct { 152 name string 153 offset uint32 154 sizeInBytes uint64 155 expected bool 156 }{ 157 { 158 name: "simple valid arguments", 159 offset: 0, // arbitrary valid offset 160 sizeInBytes: 8, // arbitrary valid size 161 expected: true, 162 }, 163 { 164 name: "maximum valid sizeInBytes", 165 offset: memory.Size() - 8, 166 sizeInBytes: 8, 167 expected: true, 168 }, 169 { 170 name: "sizeInBytes exceeds the valid size by 1", 171 offset: 100, // arbitrary valid offset 172 sizeInBytes: uint64(memory.Size() - 99), 173 expected: false, 174 }, 175 { 176 name: "offset exceeds the memory size", 177 offset: memory.Size(), 178 sizeInBytes: 1, // arbitrary size 179 expected: false, 180 }, 181 { 182 name: "offset + sizeInBytes overflows in uint32", 183 offset: math.MaxUint32 - 1, // invalid too large offset 184 sizeInBytes: 4, // if there's overflow, offset + sizeInBytes is 3, and it may pass the check 185 expected: false, 186 }, 187 { 188 name: "address.wast:200", 189 offset: 4294967295, 190 sizeInBytes: 1, 191 expected: false, 192 }, 193 } 194 195 for _, tt := range tests { 196 tc := tt 197 198 t.Run(tc.name, func(t *testing.T) { 199 require.Equal(t, tc.expected, memory.hasSize(tc.offset, tc.sizeInBytes)) 200 }) 201 } 202 } 203 204 func TestMemoryInstance_ReadUint16Le(t *testing.T) { 205 tests := []struct { 206 name string 207 memory []byte 208 offset uint32 209 expected uint16 210 expectedOk bool 211 }{ 212 { 213 name: "valid offset with an endian-insensitive v", 214 memory: []byte{0xff, 0xff}, 215 offset: 0, // arbitrary valid offset. 216 expected: math.MaxUint16, 217 expectedOk: true, 218 }, 219 { 220 name: "valid offset with an endian-sensitive v", 221 memory: []byte{0xfe, 0xff}, 222 offset: 0, // arbitrary valid offset. 223 expected: math.MaxUint16 - 1, 224 expectedOk: true, 225 }, 226 { 227 name: "maximum boundary valid offset", 228 offset: 1, 229 memory: []byte{0x00, 0x1, 0x00}, 230 expected: 1, // arbitrary valid v 231 expectedOk: true, 232 }, 233 { 234 name: "offset exceeds the maximum valid offset by 1", 235 memory: []byte{0xff, 0xff}, 236 offset: 1, 237 }, 238 } 239 240 for _, tt := range tests { 241 tc := tt 242 243 t.Run(tc.name, func(t *testing.T) { 244 memory := &MemoryInstance{Buffer: tc.memory} 245 246 v, ok := memory.ReadUint16Le(tc.offset) 247 require.Equal(t, tc.expectedOk, ok) 248 require.Equal(t, tc.expected, v) 249 }) 250 } 251 } 252 253 func TestMemoryInstance_ReadUint32Le(t *testing.T) { 254 tests := []struct { 255 name string 256 memory []byte 257 offset uint32 258 expected uint32 259 expectedOk bool 260 }{ 261 { 262 name: "valid offset with an endian-insensitive v", 263 memory: []byte{0xff, 0xff, 0xff, 0xff}, 264 offset: 0, // arbitrary valid offset. 265 expected: math.MaxUint32, 266 expectedOk: true, 267 }, 268 { 269 name: "valid offset with an endian-sensitive v", 270 memory: []byte{0xfe, 0xff, 0xff, 0xff}, 271 offset: 0, // arbitrary valid offset. 272 expected: math.MaxUint32 - 1, 273 expectedOk: true, 274 }, 275 { 276 name: "maximum boundary valid offset", 277 offset: 1, 278 memory: []byte{0x00, 0x1, 0x00, 0x00, 0x00}, 279 expected: 1, // arbitrary valid v 280 expectedOk: true, 281 }, 282 { 283 name: "offset exceeds the maximum valid offset by 1", 284 memory: []byte{0xff, 0xff, 0xff, 0xff}, 285 offset: 1, 286 }, 287 } 288 289 for _, tt := range tests { 290 tc := tt 291 292 t.Run(tc.name, func(t *testing.T) { 293 memory := &MemoryInstance{Buffer: tc.memory} 294 295 v, ok := memory.ReadUint32Le(tc.offset) 296 require.Equal(t, tc.expectedOk, ok) 297 require.Equal(t, tc.expected, v) 298 }) 299 } 300 } 301 302 func TestMemoryInstance_ReadUint64Le(t *testing.T) { 303 tests := []struct { 304 name string 305 memory []byte 306 offset uint32 307 expected uint64 308 expectedOk bool 309 }{ 310 { 311 name: "valid offset with an endian-insensitive v", 312 memory: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 313 offset: 0, // arbitrary valid offset. 314 expected: math.MaxUint64, 315 expectedOk: true, 316 }, 317 { 318 name: "valid offset with an endian-sensitive v", 319 memory: []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 320 offset: 0, // arbitrary valid offset. 321 expected: math.MaxUint64 - 1, 322 expectedOk: true, 323 }, 324 { 325 name: "maximum boundary valid offset", 326 offset: 1, 327 memory: []byte{0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 328 expected: 1, // arbitrary valid v 329 expectedOk: true, 330 }, 331 { 332 name: "offset exceeds the maximum valid offset by 1", 333 memory: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 334 offset: 1, 335 }, 336 } 337 338 for _, tt := range tests { 339 tc := tt 340 341 t.Run(tc.name, func(t *testing.T) { 342 memory := &MemoryInstance{Buffer: tc.memory} 343 344 v, ok := memory.ReadUint64Le(tc.offset) 345 require.Equal(t, tc.expectedOk, ok) 346 require.Equal(t, tc.expected, v) 347 }) 348 } 349 } 350 351 func TestMemoryInstance_ReadFloat32Le(t *testing.T) { 352 tests := []struct { 353 name string 354 memory []byte 355 offset uint32 356 expected float32 357 expectedOk bool 358 }{ 359 { 360 name: "valid offset with an endian-insensitive v", 361 memory: []byte{0xff, 0x00, 0x00, 0xff}, 362 offset: 0, // arbitrary valid offset. 363 expected: math.Float32frombits(uint32(0xff0000ff)), 364 expectedOk: true, 365 }, 366 { 367 name: "valid offset with an endian-sensitive v", 368 memory: []byte{0xfe, 0x00, 0x00, 0xff}, 369 offset: 0, // arbitrary valid offset. 370 expected: math.Float32frombits(uint32(0xff0000fe)), 371 expectedOk: true, 372 }, 373 { 374 name: "maximum boundary valid offset", 375 offset: 1, 376 memory: []byte{0x00, 0xcd, 0xcc, 0xcc, 0x3d}, 377 expected: 0.1, // arbitrary valid v 378 expectedOk: true, 379 }, 380 { 381 name: "offset exceeds the maximum valid offset by 1", 382 memory: []byte{0xff, 0xff, 0xff, 0xff}, 383 offset: 1, 384 }, 385 } 386 387 for _, tt := range tests { 388 tc := tt 389 390 t.Run(tc.name, func(t *testing.T) { 391 memory := &MemoryInstance{Buffer: tc.memory} 392 393 v, ok := memory.ReadFloat32Le(tc.offset) 394 require.Equal(t, tc.expectedOk, ok) 395 require.Equal(t, tc.expected, v) 396 }) 397 } 398 } 399 400 func TestMemoryInstance_ReadFloat64Le(t *testing.T) { 401 tests := []struct { 402 name string 403 memory []byte 404 offset uint32 405 expected float64 406 expectedOk bool 407 }{ 408 { 409 name: "valid offset with an endian-insensitive v", 410 memory: []byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, 411 offset: 0, // arbitrary valid offset. 412 expected: math.Float64frombits(uint64(0xff000000000000ff)), 413 expectedOk: true, 414 }, 415 { 416 name: "valid offset with an endian-sensitive v", 417 memory: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f}, 418 offset: 0, // arbitrary valid offset. 419 expected: math.MaxFloat64, // arbitrary valid v 420 expectedOk: true, 421 }, 422 { 423 name: "maximum boundary valid offset", 424 offset: 1, 425 memory: []byte{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f}, 426 expected: math.MaxFloat64, // arbitrary valid v 427 expectedOk: true, 428 }, 429 { 430 name: "offset exceeds the maximum valid offset by 1", 431 memory: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 432 offset: 1, 433 }, 434 } 435 436 for _, tt := range tests { 437 tc := tt 438 439 t.Run(tc.name, func(t *testing.T) { 440 memory := &MemoryInstance{Buffer: tc.memory} 441 442 v, ok := memory.ReadFloat64Le(tc.offset) 443 require.Equal(t, tc.expectedOk, ok) 444 require.Equal(t, tc.expected, v) 445 }) 446 } 447 } 448 449 func TestMemoryInstance_Read(t *testing.T) { 450 mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1} 451 452 buf, ok := mem.Read(4, 4) 453 require.True(t, ok) 454 require.Equal(t, []byte{16, 0, 0, 0}, buf) 455 456 // Test write-through 457 buf[3] = 4 458 require.Equal(t, []byte{16, 0, 0, 4}, buf) 459 require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer) 460 461 _, ok = mem.Read(5, 4) 462 require.False(t, ok) 463 464 _, ok = mem.Read(9, 4) 465 require.False(t, ok) 466 } 467 468 func TestMemoryInstance_WriteUint16Le(t *testing.T) { 469 memory := &MemoryInstance{Buffer: make([]byte, 100)} 470 471 tests := []struct { 472 name string 473 offset uint32 474 v uint16 475 expectedOk bool 476 expectedBytes []byte 477 }{ 478 { 479 name: "valid offset with an endian-insensitive v", 480 offset: 0, // arbitrary valid offset. 481 v: math.MaxUint16, 482 expectedOk: true, 483 expectedBytes: []byte{0xff, 0xff}, 484 }, 485 { 486 name: "valid offset with an endian-sensitive v", 487 offset: 0, // arbitrary valid offset. 488 v: math.MaxUint16 - 1, 489 expectedOk: true, 490 expectedBytes: []byte{0xfe, 0xff}, 491 }, 492 { 493 name: "maximum boundary valid offset", 494 offset: memory.Size() - 2, // 2 is the size of uint16 495 v: 1, // arbitrary valid v 496 expectedOk: true, 497 expectedBytes: []byte{0x1, 0x00}, 498 }, 499 { 500 name: "offset exceeds the maximum valid offset by 1", 501 offset: memory.Size() - 2 + 1, // 2 is the size of uint16 502 v: 1, // arbitrary valid v 503 expectedBytes: []byte{0xff, 0xff}, 504 }, 505 } 506 507 for _, tt := range tests { 508 tc := tt 509 510 t.Run(tc.name, func(t *testing.T) { 511 require.Equal(t, tc.expectedOk, memory.WriteUint16Le(tc.offset, tc.v)) 512 if tc.expectedOk { 513 require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+2]) // 2 is the size of uint16 514 } 515 }) 516 } 517 } 518 519 func TestMemoryInstance_WriteUint32Le(t *testing.T) { 520 memory := &MemoryInstance{Buffer: make([]byte, 100)} 521 522 tests := []struct { 523 name string 524 offset uint32 525 v uint32 526 expectedOk bool 527 expectedBytes []byte 528 }{ 529 { 530 name: "valid offset with an endian-insensitive v", 531 offset: 0, // arbitrary valid offset. 532 v: math.MaxUint32, 533 expectedOk: true, 534 expectedBytes: []byte{0xff, 0xff, 0xff, 0xff}, 535 }, 536 { 537 name: "valid offset with an endian-sensitive v", 538 offset: 0, // arbitrary valid offset. 539 v: math.MaxUint32 - 1, 540 expectedOk: true, 541 expectedBytes: []byte{0xfe, 0xff, 0xff, 0xff}, 542 }, 543 { 544 name: "maximum boundary valid offset", 545 offset: memory.Size() - 4, // 4 is the size of uint32 546 v: 1, // arbitrary valid v 547 expectedOk: true, 548 expectedBytes: []byte{0x1, 0x00, 0x00, 0x00}, 549 }, 550 { 551 name: "offset exceeds the maximum valid offset by 1", 552 offset: memory.Size() - 4 + 1, // 4 is the size of uint32 553 v: 1, // arbitrary valid v 554 expectedBytes: []byte{0xff, 0xff, 0xff, 0xff}, 555 }, 556 } 557 558 for _, tt := range tests { 559 tc := tt 560 561 t.Run(tc.name, func(t *testing.T) { 562 require.Equal(t, tc.expectedOk, memory.WriteUint32Le(tc.offset, tc.v)) 563 if tc.expectedOk { 564 require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+4]) // 4 is the size of uint32 565 } 566 }) 567 } 568 } 569 570 func TestMemoryInstance_WriteUint64Le(t *testing.T) { 571 memory := &MemoryInstance{Buffer: make([]byte, 100)} 572 tests := []struct { 573 name string 574 offset uint32 575 v uint64 576 expectedOk bool 577 expectedBytes []byte 578 }{ 579 { 580 name: "valid offset with an endian-insensitive v", 581 offset: 0, // arbitrary valid offset. 582 v: math.MaxUint64, 583 expectedOk: true, 584 expectedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 585 }, 586 { 587 name: "valid offset with an endian-sensitive v", 588 offset: 0, // arbitrary valid offset. 589 v: math.MaxUint64 - 1, 590 expectedOk: true, 591 expectedBytes: []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 592 }, 593 { 594 name: "maximum boundary valid offset", 595 offset: memory.Size() - 8, // 8 is the size of uint64 596 v: 1, // arbitrary valid v 597 expectedOk: true, 598 expectedBytes: []byte{0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 599 }, 600 { 601 name: "offset exceeds the maximum valid offset by 1", 602 offset: memory.Size() - 8 + 1, // 8 is the size of uint64 603 v: 1, // arbitrary valid v 604 expectedOk: false, 605 }, 606 } 607 608 for _, tt := range tests { 609 tc := tt 610 611 t.Run(tc.name, func(t *testing.T) { 612 require.Equal(t, tc.expectedOk, memory.WriteUint64Le(tc.offset, tc.v)) 613 if tc.expectedOk { 614 require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+8]) // 8 is the size of uint64 615 } 616 }) 617 } 618 } 619 620 func TestMemoryInstance_WriteFloat32Le(t *testing.T) { 621 memory := &MemoryInstance{Buffer: make([]byte, 100)} 622 623 tests := []struct { 624 name string 625 offset uint32 626 v float32 627 expectedOk bool 628 expectedBytes []byte 629 }{ 630 { 631 name: "valid offset with an endian-insensitive v", 632 offset: 0, // arbitrary valid offset. 633 v: math.Float32frombits(uint32(0xff0000ff)), 634 expectedOk: true, 635 expectedBytes: []byte{0xff, 0x00, 0x00, 0xff}, 636 }, 637 { 638 name: "valid offset with an endian-sensitive v", 639 offset: 0, // arbitrary valid offset. 640 v: math.Float32frombits(uint32(0xff0000fe)), // arbitrary valid v 641 expectedOk: true, 642 expectedBytes: []byte{0xfe, 0x00, 0x00, 0xff}, 643 }, 644 { 645 name: "maximum boundary valid offset", 646 offset: memory.Size() - 4, // 4 is the size of float32 647 v: 0.1, // arbitrary valid v 648 expectedOk: true, 649 expectedBytes: []byte{0xcd, 0xcc, 0xcc, 0x3d}, 650 }, 651 { 652 name: "offset exceeds the maximum valid offset by 1", 653 offset: memory.Size() - 4 + 1, // 4 is the size of float32 654 v: math.MaxFloat32, // arbitrary valid v 655 expectedBytes: []byte{0xff, 0xff, 0xff, 0xff}, 656 }, 657 } 658 659 for _, tt := range tests { 660 tc := tt 661 662 t.Run(tc.name, func(t *testing.T) { 663 require.Equal(t, tc.expectedOk, memory.WriteFloat32Le(tc.offset, tc.v)) 664 if tc.expectedOk { 665 require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+4]) // 4 is the size of float32 666 } 667 }) 668 } 669 } 670 671 func TestMemoryInstance_WriteFloat64Le(t *testing.T) { 672 memory := &MemoryInstance{Buffer: make([]byte, 100)} 673 tests := []struct { 674 name string 675 offset uint32 676 v float64 677 expectedOk bool 678 expectedBytes []byte 679 }{ 680 { 681 name: "valid offset with an endian-insensitive v", 682 offset: 0, // arbitrary valid offset. 683 v: math.Float64frombits(uint64(0xff000000000000ff)), 684 expectedOk: true, 685 expectedBytes: []byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, 686 }, 687 { 688 name: "valid offset with an endian-sensitive v", 689 offset: 0, // arbitrary valid offset. 690 v: math.MaxFloat64, // arbitrary valid v 691 expectedOk: true, 692 expectedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f}, 693 }, 694 { 695 name: "maximum boundary valid offset", 696 offset: memory.Size() - 8, // 8 is the size of float64 697 v: math.MaxFloat64, // arbitrary valid v 698 expectedOk: true, 699 expectedBytes: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f}, 700 }, 701 { 702 name: "offset exceeds the maximum valid offset by 1", 703 offset: memory.Size() - 8 + 1, // 8 is the size of float64 704 v: math.MaxFloat64, // arbitrary valid v 705 expectedOk: false, 706 }, 707 } 708 709 for _, tt := range tests { 710 tc := tt 711 712 t.Run(tc.name, func(t *testing.T) { 713 require.Equal(t, tc.expectedOk, memory.WriteFloat64Le(tc.offset, tc.v)) 714 if tc.expectedOk { 715 require.Equal(t, tc.expectedBytes, memory.Buffer[tc.offset:tc.offset+8]) // 8 is the size of float64 716 } 717 }) 718 } 719 } 720 721 func TestMemoryInstance_Write(t *testing.T) { 722 mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1} 723 724 buf := []byte{16, 0, 0, 4} 725 require.True(t, mem.Write(4, buf)) 726 require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer) 727 728 // Test it isn't write-through 729 buf[3] = 0 730 require.Equal(t, []byte{16, 0, 0, 0}, buf) 731 require.Equal(t, []byte{0, 0, 0, 0, 16, 0, 0, 4}, mem.Buffer) 732 733 ok := mem.Write(5, buf) 734 require.False(t, ok) 735 736 ok = mem.Write(9, buf) 737 require.False(t, ok) 738 } 739 740 func TestMemoryInstance_Write_overflow(t *testing.T) { 741 mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1} 742 743 // Test overflow 744 huge := uint64(math.MaxUint32 + 1 + 4) 745 if huge != uint64(int(huge)) { 746 t.Skip("Skipping on 32-bit") 747 } 748 749 buf := []byte{16, 0, 0, 4} 750 header := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) 751 header.Len = int(huge) 752 header.Cap = int(huge) 753 754 require.False(t, mem.Write(4, buf)) 755 } 756 757 func TestMemoryInstance_WriteString(t *testing.T) { 758 mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1} 759 760 s := "bear" 761 require.True(t, mem.WriteString(4, s)) 762 require.Equal(t, []byte{0, 0, 0, 0, 'b', 'e', 'a', 'r'}, mem.Buffer) 763 764 ok := mem.WriteString(5, s) 765 require.False(t, ok) 766 767 ok = mem.WriteString(9, s) 768 require.False(t, ok) 769 } 770 771 func BenchmarkWriteString(b *testing.B) { 772 tests := []string{ 773 "", 774 "bear", 775 "hello world", 776 strings.Repeat("hello ", 10), 777 } 778 //nolint intentionally testing interface access 779 var mem api.Memory 780 mem = &MemoryInstance{Buffer: make([]byte, 1000), Min: 1} 781 for _, tt := range tests { 782 b.Run("", func(b *testing.B) { 783 b.Run("Write", func(b *testing.B) { 784 for i := 0; i < b.N; i++ { 785 if !mem.Write(0, []byte(tt)) { 786 b.Fail() 787 } 788 } 789 }) 790 b.Run("WriteString", func(b *testing.B) { 791 for i := 0; i < b.N; i++ { 792 if !mem.WriteString(0, tt) { 793 b.Fail() 794 } 795 } 796 }) 797 }) 798 } 799 }