0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2025-02-16 16:50:12 +01:00

ircd::utf: Split header; improve decode codegen; inline length.

This commit is contained in:
Jason Volk 2021-08-11 04:31:32 -07:00
parent 880f089169
commit 3e6fcf3a47
4 changed files with 139 additions and 85 deletions

View file

@ -72,7 +72,8 @@
#include "crh.h" #include "crh.h"
#include "fpe.h" #include "fpe.h"
#include "icu.h" #include "icu.h"
#include "utf.h" #include "utf8.h"
#include "utf16.h"
#include "b64.h" #include "b64.h"
#include "b58.h" #include "b58.h"
#include "iov.h" #include "iov.h"

View file

@ -1,7 +1,7 @@
// The Construct // The Construct
// //
// Copyright (C) The Construct Developers, Authors & Contributors // Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2020 Jason Volk <jason@zemos.net> // Copyright (C) 2016-2021 Jason Volk <jason@zemos.net>
// //
// Permission to use, copy, modify, and/or distribute this software for any // Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above // purpose with or without fee is hereby granted, provided that the above
@ -9,26 +9,7 @@
// full license for this software is available in the LICENSE file. // full license for this software is available in the LICENSE file.
#pragma once #pragma once
#define HAVE_IRCD_UTF_H #define HAVE_IRCD_UTF16_H
/// Unicode Transformation Format
namespace ircd::utf
{
IRCD_EXCEPTION(ircd::error, error)
}
/// Unicode Transformation Format (8-bit)
namespace ircd::utf8
{
// Get the utf8-encoded length from char32_t (decoded) codepoints
template<class u32xN> u32xN length(const u32xN codepoints) noexcept;
// Encode char32_t codepoints into respective utf-8 encodings
template<class u32xN> u32xN encode_sparse(const u32xN codepoints) noexcept;
// Decode utf-8 string into char32_t unicode codepoints
u32x16 decode(const u8x16 string) noexcept;
}
/// Unicode Transformation Format (16-bit) /// Unicode Transformation Format (16-bit)
namespace ircd::utf16 namespace ircd::utf16

57
include/ircd/utf8.h Normal file
View file

@ -0,0 +1,57 @@
// The Construct
//
// Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2021 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#pragma once
#define HAVE_IRCD_UTF8_H
/// Unicode Transformation Format (8-bit)
namespace ircd::utf8
{
// Get the utf8-encoded length from char32_t (decoded) codepoints
template<class u32xN> u32xN length(const u32xN codepoints) noexcept;
// Get the utf8 length at the first byte of each utf8-codepoint; 0x00 sep
template<> u8x16 length(const u8x16 string) noexcept;
// Encode char32_t codepoints into respective utf-8 encodings
template<class u32xN> u32xN encode_sparse(const u32xN codepoints) noexcept;
// Decode utf-8 string into char32_t unicode codepoints
u32x16 decode_sparse(const u8x16 string) noexcept;
// Decode utf-8 string into char32_t unicode codepoints packed left.
u32x16 decode(const u8x16 string) noexcept;
}
template<>
inline ircd::u8x16
ircd::utf8::length(const u8x16 string)
noexcept
{
const u8x16 is_single
(
string < 0x80
);
const u8x16 is_multi
(
(string - 0xc2) <= 0x32
);
const u8x16 num_trail
(
1
+ ((string >= 0xc0) & 1)
+ ((string >= 0xe0) & 1)
+ ((string >= 0xf0) & 1)
);
return (is_single & 1) | (is_multi & num_trail);
}

View file

@ -302,100 +302,115 @@ noexcept
// //
ircd::u32x16 ircd::u32x16
ircd::utf8::decode(const u8x16 in) ircd::utf8::decode(const u8x16 string)
noexcept noexcept
{ {
const u8x16 is_single const u32x16 codepoints
( (
(in & 0x80) == 0 decode_sparse(string)
); );
const u8x16 is_lead const i32x16 zero_lane
( (
(in - 0xc2) <= 0x32 codepoints == 0
); );
const u8x16 is_trail // Lanes separating sparsely decoded codepoints are zero.
const i8x16 skip_lane
( (
in >= 0x80 && in < 0xbf lane_cast<i8x16>(zero_lane)
); );
const u8x16 is_head // Actual NUL codepoints weren't altered by decode.
const i8x16 null_code
( (
is_lead | is_single string == 0
); );
const u8x16 len_mask[3] // The pack will eliminate zero-value lanes except for legitimate NULs.
{ const i8x16 pack_mask
in >= 0xc0, in >= 0xe0, in >= 0xf0,
};
const u8x16 expect_trail
( (
1 + (len_mask[0] & 1) + (len_mask[1] & 1) + (len_mask[2] & 1) ~null_code ^ skip_lane
); );
const u32x16 ret
(
simd::pack(codepoints, pack_mask)
);
return ret;
}
ircd::u32x16
ircd::utf8::decode_sparse(const u8x16 string)
noexcept
{
const u8x16 len const u8x16 len
( (
(is_single & 1) | (is_lead & expect_trail) length(string)
); );
const u8x16 head const u8x16 rem
( (
in & is_head len
| ((shl<0x18>(len) == 4) & 1)
| ((shl<0x10>(len) == 4) & 2)
| ((shl<0x10>(len) == 3) & 1)
| ((shl<0x08>(len) == 4) & 3)
| ((shl<0x08>(len) == 3) & 2)
| ((shl<0x08>(len) == 2) & 1)
); );
const u8x16 lead[] const u8x16 bank[]
{ {
0x3f & in & is_trail, string & 0x3f,
0xff & head & is_single, string & 0xff,
0x1f & head & len_mask[0] & ~len_mask[1], string & 0x1f,
0x0f & head & len_mask[1] & ~len_mask[2], string & 0x0f,
0x07 & head & len_mask[2], string & 0x07,
}; };
u8x16 full; const u8x16 select
for(uint i(0); i < 16; ++i)
full[i] = lead[len[i]][i];
u8x16 shift {len & is_head};
shift |= (shl<0x20>(len) == 4) & 0;
shift |= (shl<0x20>(len) == 3) & 0;
shift |= (shl<0x20>(len) == 2) & 0;
shift |= (shl<0x20>(len) == 1) & 0;
shift |= (shl<0x18>(len) == 4) & 1;
shift |= (shl<0x18>(len) == 3) & 0;
shift |= (shl<0x18>(len) == 2) & 0;
shift |= (shl<0x18>(len) == 1) & 0;
shift |= (shl<0x10>(len) == 4) & 2;
shift |= (shl<0x10>(len) == 3) & 1;
shift |= (shl<0x10>(len) == 2) & 0;
shift |= (shl<0x10>(len) == 1) & 0;
shift |= (shl<0x08>(len) == 4) & 3;
shift |= (shl<0x08>(len) == 3) & 2;
shift |= (shl<0x08>(len) == 2) & 1;
shift |= (shl<0x08>(len) == 1) & 0;
shift -= 1U;
shift &= 0x03U;
shift *= 6U;
const u32x16 val
{
lane_cast<u32x16>(full) << lane_cast<u32x16>(shift)
};
const u8x16 incr
( (
(shift == 0) & 1U 0
| (bank[0] & (len == 0))
| (bank[1] & (len == 1))
| (bank[2] & (len == 2))
| (bank[3] & (len == 3))
| (bank[4] & (len == 4))
); );
u8x16 idx {0}; const u8x16 byte[]
for(uint i(1); i < 16; ++i) {
idx[i] = idx[i - 1] + incr[i - 1]; select & (rem == 1),
select & (rem == 2),
select & (rem == 3),
select & (rem == 4),
};
u32x16 ret {0}; const u8x16 move[]
for(uint i(0); i < 16; ++i) {
ret[idx[i]] |= val[i]; shl<8 * 0>(byte[0]),
shl<8 * 1>(byte[1]),
shl<8 * 2>(byte[2]),
shl<8 * 3>(byte[3]),
};
const u32x16 pack[]
{
lane_cast<u32x16>(move[0]) << 0x00,
lane_cast<u32x16>(move[1]) << 0x06,
lane_cast<u32x16>(move[2]) << 0x0c,
lane_cast<u32x16>(move[3]) << 0x12,
};
const u32x16 ret
(
lane_cast<u32x16>(byte[0]) // pack[0] constrains clang opt
| pack[1]
| pack[2]
| pack[3]
);
return ret; return ret;
} }