| ... | @@ -542,7 +542,6 @@ test "utf16leToUtf8" { | ... | @@ -542,7 +542,6 @@ test "utf16leToUtf8" { |
| 542 | } | 542 | } |
| 543 | } | 543 | } |
| 544 | | 544 | |
| 545 | /// TODO support codepoints bigger than 16 bits | | |
| 546 | /// TODO type for null terminated pointer | 545 | /// TODO type for null terminated pointer |
| 547 | pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 { | 546 | pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 { |
| 548 | var result = std.ArrayList(u16).init(allocator); | 547 | var result = std.ArrayList(u16).init(allocator); |
| ... | @@ -552,7 +551,18 @@ pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 | ... | @@ -552,7 +551,18 @@ pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 |
| 552 | const view = try Utf8View.init(utf8); | 551 | const view = try Utf8View.init(utf8); |
| 553 | var it = view.iterator(); | 552 | var it = view.iterator(); |
| 554 | while (it.nextCodepoint()) |codepoint| { | 553 | while (it.nextCodepoint()) |codepoint| { |
| 555 | try result.append(@intCast(u16, codepoint)); // TODO surrogate pairs | 554 | if (codepoint < 0x10000) { |
| | 555 | const short = @intCast(u16, codepoint); |
| | 556 | try result.append(mem.nativeToLittle(u16, short)); |
| | 557 | } else { |
| | 558 | const short = @intCast(u16, codepoint - 0x10000); |
| | 559 | const high = (short >> 10) + 0xD800; |
| | 560 | const low = (short & 0x3FF) + 0xDC00; |
| | 561 | var out: [2]u16 = undefined; |
| | 562 | out[0] = mem.nativeToLittle(u16, high); |
| | 563 | out[1] = mem.nativeToLittle(u16, low); |
| | 564 | try result.appendSlice(out[0..]); |
| | 565 | } |
| 556 | } | 566 | } |
| 557 | | 567 | |
| 558 | try result.append(0); | 568 | try result.append(0); |
| ... | @@ -561,7 +571,6 @@ pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 | ... | @@ -561,7 +571,6 @@ pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 |
| 561 | | 571 | |
| 562 | /// Returns index of next character. If exact fit, returned index equals output slice length. | 572 | /// Returns index of next character. If exact fit, returned index equals output slice length. |
| 563 | /// Assumes there is enough space for the output. | 573 | /// Assumes there is enough space for the output. |
| 564 | /// TODO support codepoints bigger than 16 bits | | |
| 565 | pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { | 574 | pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { |
| 566 | var dest_i: usize = 0; | 575 | var dest_i: usize = 0; |
| 567 | var src_i: usize = 0; | 576 | var src_i: usize = 0; |
| ... | @@ -578,12 +587,18 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { | ... | @@ -578,12 +587,18 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { |
| 578 | 2, 3, 4 => { | 587 | 2, 3, 4 => { |
| 579 | const next_src_i = src_i + n; | 588 | const next_src_i = src_i + n; |
| 580 | const codepoint = utf8Decode(utf8[src_i..next_src_i]) catch return error.InvalidUtf8; | 589 | const codepoint = utf8Decode(utf8[src_i..next_src_i]) catch return error.InvalidUtf8; |
| 581 | const short = @intCast(u16, codepoint); // TODO surrogate pairs | 590 | if (codepoint < 0x10000) { |
| 582 | utf16le[dest_i] = switch (builtin.endian) { | 591 | const short = @intCast(u16, codepoint); |
| 583 | .Little => short, | 592 | utf16le[dest_i] = mem.nativeToLittle(u16, short); |
| 584 | .Big => @byteSwap(u16, short), | 593 | dest_i += 1; |
| 585 | }; | 594 | } else { |
| 586 | dest_i += 1; | 595 | const short = @intCast(u16, codepoint - 0x10000); |
| | 596 | const high = (short >> 10) + 0xD800; |
| | 597 | const low = (short & 0x3FF) + 0xDC00; |
| | 598 | utf16le[dest_i] = mem.nativeToLittle(u16, high); |
| | 599 | utf16le[dest_i + 1] = mem.nativeToLittle(u16, low); |
| | 600 | dest_i += 2; |
| | 601 | } |
| 587 | src_i = next_src_i; | 602 | src_i = next_src_i; |
| 588 | }, | 603 | }, |
| 589 | else => return error.InvalidUtf8, | 604 | else => return error.InvalidUtf8, |
| ... | @@ -591,3 +606,17 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { | ... | @@ -591,3 +606,17 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { |
| 591 | } | 606 | } |
| 592 | return dest_i; | 607 | return dest_i; |
| 593 | } | 608 | } |
| | 609 | |
| | 610 | test "utf8ToUtf16Le" { |
| | 611 | var utf16le: [2]u16 = [_]u16{0} ** 2; |
| | 612 | const length = try utf8ToUtf16Le(utf16le[0..], "𐐷"); |
| | 613 | testing.expect(@as(usize, 2) == length); |
| | 614 | testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc", @sliceToBytes(utf16le[0..])); |
| | 615 | } |
| | 616 | |
| | 617 | test "utf8ToUtf16LeWithNull" { |
| | 618 | var bytes: [128]u8 = undefined; |
| | 619 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; |
| | 620 | const utf16 = try utf8ToUtf16LeWithNull(allocator, "𐐷"); |
| | 621 | testing.expectEqualSlices(u8, "\x01\xd8\x37\xdc\x00\x00", @sliceToBytes(utf16[0..])); |
| | 622 | } |