blob: 752fc6a46c531fdb38437b3318a23338255515b7 [file]
/* See LICENSE file for copyright and license details. */
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "../gen/bidirectional.h"
#include "../grapheme.h"
#include "util.h"
#define MAX_DEPTH 125
struct isolate_runner {
int_least32_t *buf;
size_t buflen;
enum bidi_property prev_prop;
struct {
size_t off;
enum bidi_property prop;
int_least8_t level;
} cur;
struct {
size_t off;
enum bidi_property prop;
} next;
int_least8_t paragraph_level;
int_least8_t isolating_run_level;
enum bidi_property last_strong_type;
};
/*
* we want to store the bidirectional property, the embedding level
* and the visited-state (for sequential processing of rule X10)
* all in a signed 16-bit-integer (because that's what we will write
* the final embedding level into). To remain outside of implementation
* defined territory, we can only effectively use 15 bits for bitwise
* magic.
*
* Storage is still feasible, though, because the values all have very
* limited ranges and can be stored as unsigned integers:
*
* level+1 is in 0..MAX_DEPTH+1=126 which lies in 0..127 = 2^7-1
* visited is in 0..1 which lies in 0..1 = 2^1-1
* prop is in 0..23 which lies in 0..31 = 2^5-1
* rawprop is in 0..23 which lies in 0..31 = 2^5-1
*
* yielding a total storage size of 18 bits.
*
* TODO: 7 bits for 0..127 bracket types, 1 bit for resolved paragraph
*
* grapheme_bidirectional_preprocess(src, srclen, override, data, datalen)
* grapheme_get_bidirectional_line_embedding_levels(data, datalen, lev, levlen)
*
* grapheme_get_bidirectional_line_levels
* grapheme_bidirectional_reorder_line
*/
static inline int_least8_t
buffy_get_level(int_least32_t val)
{
return ((int_least8_t)(((uint_least32_t)val) & 0x7F /* 01111111 */)) - 1;
}
static inline void
buffy_set_level(int_least32_t *val, int_least8_t level)
{
*val = (((uint_least32_t)(*val)) & UINT32_C(0x3FF80) /* 00000011.11111111.10000000 */) |
((uint_least8_t)(level + 1) & 0x7F /* 01111111 */);
}
static inline bool
buffy_is_visited(int_least32_t val)
{
return (((uint_least32_t)val) & 0x80); /* 10000000 */
}
static inline void
buffy_set_visited(int_least32_t *val, bool visited)
{
*val = (int_least32_t)(
(((uint_least32_t)(*val)) & UINT32_C(0x3FF7F) /* 00000011.11111111.01111111 */) |
(((uint_least32_t)(visited)) << 7));
}
static inline enum bidi_property
buffy_get_prop(int_least32_t val)
{
return (((uint_least32_t)val) & UINT32_C(0x1F00) /* 00011111.00000000 */) >> 8;
}
static inline void
buffy_set_prop(int_least32_t *val, enum bidi_property prop)
{
*val = (int_least32_t)(
(((uint_least32_t)(*val)) & UINT32_C(0x3E0FF) /* 00000011.11100000.11111111 */) |
(((uint_least32_t)((uint_least8_t)(prop)) & 0x1F /* 00011111 */) << 8));
}
static inline enum bidi_property
buffy_get_rawprop(int_least32_t val)
{
return (((uint_least32_t)val) & UINT32_C(0x3E000) /* 00000011.11100000.00000000 */) >> 13;
}
static inline void
buffy_set_rawprop(int_least32_t *val, enum bidi_property rawprop)
{
*val = (int_least32_t)(
(((uint_least32_t)(*val)) & UINT32_C(0x1FFF) /* 00011111.11111111 */) |
(((uint_least32_t)((uint_least8_t)(rawprop)) & 0x1F /* 00011111 */) << 13));
}
static void
isolate_runner_init(int_least32_t *buf, size_t buflen, size_t off,
int_least8_t paragraph_level, bool within,
struct isolate_runner *ir)
{
ssize_t i;
int_least8_t cur_level, sos_level, level;
/* initialize invariants */
ir->buf = buf;
ir->buflen = buflen;
ir->paragraph_level = paragraph_level;
ir->isolating_run_level = buffy_get_level(buf[off]);
/* advance off until we are at a non-removed character */
while (buffy_get_level(buf[off]) == -1) {
off++;
}
/*
* we store the current offset in the next offset, so it is
* shifted in properly at the first advancement
*/
ir->next.off = off;
ir->next.prop = buffy_get_prop(buf[off]);
/*
* determine the previous state but store it in cur.prop
* cur.off is set to SIZE_MAX and cur.level to -1, as both are
* discarded on the first advancement anyway
*/
cur_level = buffy_get_level(buf[off]);
ir->cur.prop = NUM_BIDI_PROPS;
for (i = (ssize_t)off - 1, sos_level = -1; i >= 0; i--) {
level = buffy_get_level(buf[i]);
if (level != -1) {
/*
* we found a character that has not been
* removed in X9
*/
sos_level = level;
if (within) {
/* we just take it */
ir->cur.prop = buffy_get_prop(buf[i]);
}
break;
}
}
if (sos_level == -1) {
/*
* there were no preceding characters, set sos-level
* to paragraph embedding level
*/
sos_level = paragraph_level;
}
if (!within || ir->cur.prop == NUM_BIDI_PROPS) {
/*
* we are at the beginning of the sequence; initialize
* it faithfully according to the algorithm by looking
* at the sos-level
*/
if (MAX(sos_level, cur_level) % 2 == 0) {
/* the higher level is even, set sos to L */
ir->cur.prop = BIDI_PROP_L;
} else {
/* the higher level is odd, set sos to R */
ir->cur.prop = BIDI_PROP_R;
}
}
ir->cur.off = SIZE_MAX;
ir->cur.level = -1;
}
static int
isolate_runner_advance(struct isolate_runner *ir)
{
enum bidi_property prop;
int_least8_t level, isolate_level, last_isolate_level;
size_t i;
if (ir->next.off == SIZE_MAX) {
/* the sequence is over */
return 1;
}
/* shift in */
ir->prev_prop = ir->cur.prop;
ir->cur.off = ir->next.off;
ir->cur.prop = ir->next.prop;
ir->cur.level = buffy_get_level(ir->buf[ir->cur.off]);
/* mark as visited */
buffy_set_visited(&(ir->buf[ir->cur.off]), true);
/*
* update last strong type, which is guaranteed to work properly
* on the first advancement as the prev_prop holds the sos type,
* which can only be either R or L, which are both strong types
*/
if (ir->prev_prop == BIDI_PROP_R ||
ir->prev_prop == BIDI_PROP_L ||
ir->prev_prop == BIDI_PROP_AL) {
ir->last_strong_type = ir->prev_prop;
}
/* initialize next state by going to the next character in the sequence */
ir->next.off = SIZE_MAX;
ir->next.prop = NUM_BIDI_PROPS;
last_isolate_level = -1;
for (i = ir->cur.off, isolate_level = 0; i < ir->buflen; i++) {
prop = buffy_get_prop(ir->buf[i]);
level = buffy_get_level(ir->buf[i]);
if (level == -1) {
/* this is one of the ignored characters, skip */
continue;
} else if (level == ir->isolating_run_level) {
last_isolate_level = level;
}
/* follow BD8/BD9 and P2 to traverse the current sequence */
if (prop == BIDI_PROP_LRI ||
prop == BIDI_PROP_RLI ||
prop == BIDI_PROP_FSI) {
/*
* we encountered an isolate initiator, increment
* counter, but go into processing when we
* were not isolated before
*/
if (isolate_level < MAX_DEPTH) {
isolate_level++;
}
if (isolate_level != 1) {
continue;
}
} else if (prop == BIDI_PROP_PDI &&
isolate_level > 0) {
isolate_level--;
/*
* if the current PDI dropped the isolate-level
* to zero, it is itself part of the isolating
* run sequence; otherwise we simply continue.
*/
if (isolate_level > 0) {
continue;
}
} else if (isolate_level > 0) {
/* we are in an isolating sequence */
continue;
}
/*
* now we either still are in our sequence or we hit
* the eos-case as we left the sequence and hit the
* first non-isolating-sequence character.
*/
if (i == ir->cur.off) {
/* we were in the first initializing round */
continue;
} else if (level == ir->isolating_run_level) {
/* isolate_level-skips have been handled before, we're good */
/* still in the sequence */
ir->next.off = (size_t)i;
ir->next.prop = buffy_get_prop(ir->buf[i]);
} else {
/* out of sequence or isolated, compare levels via eos */
if (MAX(last_isolate_level, level) % 2 == 0) {
ir->next.prop = BIDI_PROP_L;
} else {
ir->next.prop = BIDI_PROP_R;
}
ir->next.off = SIZE_MAX;
}
break;
}
if (i == ir->buflen) {
/*
* the sequence ended before we could grab an offset.
* we need to determine the eos-prop by comparing the
* level of the last element in the isolating run sequence
* with the paragraph level.
*/
if (MAX(last_isolate_level, ir->paragraph_level) % 2 == 0) {
/* the higher level is even, set eos to L */
ir->next.prop = BIDI_PROP_L;
} else {
/* the higher level is odd, set sos to R */
ir->next.prop = BIDI_PROP_R;
}
ir->next.off = SIZE_MAX;
}
return 0;
}
static void
isolate_runner_set_current_prop(struct isolate_runner *ir, enum bidi_property prop)
{
buffy_set_prop(&(ir->buf[ir->cur.off]), prop);
ir->cur.prop = prop;
}
static inline enum bidi_property
get_bidi_property(uint_least32_t cp)
{
if (likely(cp <= 0x10FFFF)) {
return (enum bidi_property)
bidi_minor[bidi_major[cp >> 8] + (cp & 0xff)];
} else {
return BIDI_PROP_L;
}
}
static size_t
process_isolating_run_sequence(int_least32_t *buf, size_t buflen,
size_t off, int_least8_t paragraph_level)
{
enum bidi_property sequence_prop;
struct isolate_runner ir, tmp;
size_t runsince, sequence_end;
/* W1 */
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (ir.cur.prop == BIDI_PROP_NSM) {
if (ir.prev_prop == BIDI_PROP_LRI ||
ir.prev_prop == BIDI_PROP_RLI ||
ir.prev_prop == BIDI_PROP_FSI ||
ir.prev_prop == BIDI_PROP_PDI) {
isolate_runner_set_current_prop(&ir, BIDI_PROP_ON);
} else {
isolate_runner_set_current_prop(&ir,
ir.prev_prop);
}
}
}
/* W2 */
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (ir.cur.prop == BIDI_PROP_EN &&
ir.last_strong_type == BIDI_PROP_AL) {
isolate_runner_set_current_prop(&ir, BIDI_PROP_AN);
}
}
/* W3 */
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (ir.cur.prop == BIDI_PROP_AL) {
isolate_runner_set_current_prop(&ir, BIDI_PROP_R);
}
}
/* W4 */
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (ir.prev_prop == BIDI_PROP_EN &&
(ir.cur.prop == BIDI_PROP_ES ||
ir.cur.prop == BIDI_PROP_CS) &&
ir.next.prop == BIDI_PROP_EN) {
isolate_runner_set_current_prop(&ir, BIDI_PROP_EN);
}
if (ir.prev_prop == BIDI_PROP_AN &&
ir.cur.prop == BIDI_PROP_CS &&
ir.next.prop == BIDI_PROP_AN) {
isolate_runner_set_current_prop(&ir, BIDI_PROP_AN);
}
}
/* W5 */
runsince = SIZE_MAX;
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (ir.cur.prop == BIDI_PROP_ET) {
if (runsince == SIZE_MAX) {
/* a new run has begun */
runsince = ir.cur.off;
}
} else if (ir.cur.prop == BIDI_PROP_EN) {
/* set the preceding sequence */
if (runsince != SIZE_MAX) {
isolate_runner_init(buf, buflen, runsince, paragraph_level, (runsince > off), &tmp);
while (!isolate_runner_advance(&tmp) &&
tmp.cur.off < ir.cur.off) {
isolate_runner_set_current_prop(&tmp, BIDI_PROP_EN);
}
runsince = SIZE_MAX;
} else {
isolate_runner_init(buf, buflen, ir.cur.off, paragraph_level, (ir.cur.off > off), &tmp);
isolate_runner_advance(&tmp);
}
/* follow the succeeding sequence */
while (!isolate_runner_advance(&tmp)) {
if (tmp.cur.prop != BIDI_PROP_ET) {
break;
}
isolate_runner_set_current_prop(&tmp, BIDI_PROP_EN);
}
} else {
/* sequence ended */
runsince = SIZE_MAX;
}
}
/* W6 */
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (ir.cur.prop == BIDI_PROP_ES ||
ir.cur.prop == BIDI_PROP_ET ||
ir.cur.prop == BIDI_PROP_CS) {
isolate_runner_set_current_prop(&ir, BIDI_PROP_ON);
}
}
/* W7 */
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (ir.cur.prop == BIDI_PROP_EN &&
ir.last_strong_type == BIDI_PROP_L) {
isolate_runner_set_current_prop(&ir, BIDI_PROP_L);
}
}
/* N0 */
/* N1 */
sequence_end = SIZE_MAX;
sequence_prop = NUM_BIDI_PROPS;
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (sequence_end == SIZE_MAX) {
if (ir.cur.prop == BIDI_PROP_B ||
ir.cur.prop == BIDI_PROP_S ||
ir.cur.prop == BIDI_PROP_WS ||
ir.cur.prop == BIDI_PROP_ON ||
ir.cur.prop == BIDI_PROP_FSI ||
ir.cur.prop == BIDI_PROP_LRI ||
ir.cur.prop == BIDI_PROP_RLI ||
ir.cur.prop == BIDI_PROP_PDI) {
/* the current character is an NI (neutral or isolate) */
/* scan ahead to the end of the NI-sequence */
isolate_runner_init(buf, buflen, ir.cur.off, paragraph_level, (ir.cur.off > off), &tmp);
while (!isolate_runner_advance(&tmp)) {
if (tmp.next.prop != BIDI_PROP_B &&
tmp.next.prop != BIDI_PROP_S &&
tmp.next.prop != BIDI_PROP_WS &&
tmp.next.prop != BIDI_PROP_ON &&
tmp.next.prop != BIDI_PROP_FSI &&
tmp.next.prop != BIDI_PROP_LRI &&
tmp.next.prop != BIDI_PROP_RLI &&
tmp.next.prop != BIDI_PROP_PDI) {
break;
}
}
/*
* check what follows and see if the text has the
* same direction on both sides
*/
if (ir.prev_prop == BIDI_PROP_L &&
tmp.next.prop == BIDI_PROP_L) {
sequence_end = tmp.cur.off;
sequence_prop = BIDI_PROP_L;
} else if ((ir.prev_prop == BIDI_PROP_R ||
ir.prev_prop == BIDI_PROP_EN ||
ir.prev_prop == BIDI_PROP_AN) &&
(tmp.next.prop == BIDI_PROP_R ||
tmp.next.prop == BIDI_PROP_EN ||
tmp.next.prop == BIDI_PROP_AN)) {
sequence_end = tmp.cur.off;
sequence_prop = BIDI_PROP_R;
}
}
}
if (sequence_end != SIZE_MAX) {
if (ir.cur.off <= sequence_end) {
isolate_runner_set_current_prop(&ir, sequence_prop);
} else {
/* end of sequence, reset */
sequence_end = SIZE_MAX;
sequence_prop = NUM_BIDI_PROPS;
}
}
}
/* N2 */
isolate_runner_init(buf, buflen, off, paragraph_level, false, &ir);
while (!isolate_runner_advance(&ir)) {
if (ir.cur.prop == BIDI_PROP_B ||
ir.cur.prop == BIDI_PROP_S ||
ir.cur.prop == BIDI_PROP_WS ||
ir.cur.prop == BIDI_PROP_ON ||
ir.cur.prop == BIDI_PROP_FSI ||
ir.cur.prop == BIDI_PROP_LRI ||
ir.cur.prop == BIDI_PROP_RLI ||
ir.cur.prop == BIDI_PROP_PDI) {
/* N2 */
if (ir.cur.level % 2 == 0) {
/* even embedding level */
isolate_runner_set_current_prop(&ir, BIDI_PROP_L);
} else {
/* odd embedding level */
isolate_runner_set_current_prop(&ir, BIDI_PROP_R);
}
}
}
return 0;
}
static int_least8_t
get_paragraph_level(enum grapheme_bidirectional_override override,
bool terminate_on_pdi,
const int_least32_t *buf, size_t buflen)
{
enum bidi_property prop;
int_least8_t isolate_level;
size_t bufoff;
/* check overrides first according to rule HL1 */
if (override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_LTR) {
return 0;
} else if (override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_RTL) {
return 1;
}
/* determine paragraph level (rules P1-P3) */
for (bufoff = 0, isolate_level = 0; bufoff < buflen; bufoff++) {
prop = buffy_get_prop(buf[bufoff]);
if (prop == BIDI_PROP_PDI &&
isolate_level == 0 &&
terminate_on_pdi) {
/*
* we are in a FSI-subsection of a paragraph and
* matched with the terminating PDI
*/
break;
}
/* BD8/BD9 */
if ((prop == BIDI_PROP_LRI ||
prop == BIDI_PROP_RLI ||
prop == BIDI_PROP_FSI) &&
isolate_level < MAX_DEPTH) {
/* we hit an isolate initiator, increment counter */
isolate_level++;
} else if (prop == BIDI_PROP_PDI && isolate_level > 0) {
isolate_level--;
}
/* P2 */
if (isolate_level > 0) {
continue;
}
/* P3 */
if (prop == BIDI_PROP_L) {
return 0;
} else if (prop == BIDI_PROP_AL ||
prop == BIDI_PROP_R) {
return 1;
}
}
return 0;
}
static void
get_paragraph_embedding_levels(enum grapheme_bidirectional_override override,
int_least32_t *buf, size_t buflen)
{
struct {
int_least8_t level;
enum grapheme_bidirectional_override override;
bool directional_isolate;
} directional_status[MAX_DEPTH + 2], *dirstat = directional_status;
enum bidi_property prop, rawprop;
size_t overflow_isolate_count, overflow_embedding_count,
valid_isolate_count, bufoff, i, runsince;
int_least8_t paragraph_level, level;
paragraph_level = get_paragraph_level(override, false, buf, buflen);
/* X1 */
dirstat->level = paragraph_level;
dirstat->override = GRAPHEME_BIDIRECTIONAL_OVERRIDE_NEUTRAL;
dirstat->directional_isolate = false;
overflow_isolate_count = overflow_embedding_count = valid_isolate_count = 0;
for (bufoff = 0; bufoff < buflen; bufoff++) {
prop = buffy_get_prop(buf[bufoff]);
again:
if (prop == BIDI_PROP_RLE) {
/* X2 */
if (dirstat->level + (dirstat->level % 2 != 0) + 1 <= MAX_DEPTH &&
overflow_isolate_count == 0 &&
overflow_embedding_count == 0) {
/* valid RLE */
dirstat++;
dirstat->level = (dirstat - 1)->level + ((dirstat - 1)->level % 2 != 0) + 1;
dirstat->override = GRAPHEME_BIDIRECTIONAL_OVERRIDE_NEUTRAL;
dirstat->directional_isolate = false;
} else {
/* overflow RLE */
overflow_embedding_count += (overflow_isolate_count == 0);
}
} else if (prop == BIDI_PROP_LRE) {
/* X3 */
if (dirstat->level + (dirstat->level % 2 == 0) + 1 <= MAX_DEPTH &&
overflow_isolate_count == 0 &&
overflow_embedding_count == 0) {
/* valid LRE */
dirstat++;
dirstat->level = (dirstat - 1)->level + ((dirstat - 1)->level % 2 == 0) + 1;
dirstat->override = GRAPHEME_BIDIRECTIONAL_OVERRIDE_NEUTRAL;
dirstat->directional_isolate = false;
} else {
/* overflow LRE */
overflow_embedding_count += (overflow_isolate_count == 0);
}
} else if (prop == BIDI_PROP_RLO) {
/* X4 */
if (dirstat->level + (dirstat->level % 2 != 0) + 1 <= MAX_DEPTH &&
overflow_isolate_count == 0 &&
overflow_embedding_count == 0) {
/* valid RLO */
dirstat++;
dirstat->level = (dirstat - 1)->level + ((dirstat - 1)->level % 2 != 0) + 1;
dirstat->override = GRAPHEME_BIDIRECTIONAL_OVERRIDE_RTL;
dirstat->directional_isolate = false;
} else {
/* overflow RLO */
overflow_embedding_count += (overflow_isolate_count == 0);
}
} else if (prop == BIDI_PROP_LRO) {
/* X5 */
if (dirstat->level + (dirstat->level % 2 == 0) + 1 <= MAX_DEPTH &&
overflow_isolate_count == 0 &&
overflow_embedding_count == 0) {
/* valid LRE */
dirstat++;
dirstat->level = (dirstat - 1)->level + ((dirstat - 1)->level % 2 == 0) + 1;
dirstat->override = GRAPHEME_BIDIRECTIONAL_OVERRIDE_LTR;
dirstat->directional_isolate = false;
} else {
/* overflow LRO */
overflow_embedding_count += (overflow_isolate_count == 0);
}
} else if (prop == BIDI_PROP_RLI) {
/* X5a */
buffy_set_level(&(buf[bufoff]), dirstat->level);
if (dirstat->override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_LTR) {
buffy_set_prop(&(buf[bufoff]), BIDI_PROP_L);
} else if (dirstat->override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_RTL) {
buffy_set_prop(&(buf[bufoff]), BIDI_PROP_R);
}
if (dirstat->level + (dirstat->level % 2 != 0) + 1 <= MAX_DEPTH &&
overflow_isolate_count == 0 &&
overflow_embedding_count == 0) {
/* valid RLI */
valid_isolate_count++;
dirstat++;
dirstat->level = (dirstat - 1)->level + ((dirstat - 1)->level % 2 != 0) + 1;
dirstat->override = GRAPHEME_BIDIRECTIONAL_OVERRIDE_NEUTRAL;
dirstat->directional_isolate = true;
} else {
/* overflow RLI */
overflow_isolate_count++;
}
} else if (prop == BIDI_PROP_LRI) {
/* X5b */
buffy_set_level(&(buf[bufoff]), dirstat->level);
if (dirstat->override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_LTR) {
buffy_set_prop(&(buf[bufoff]), BIDI_PROP_L);
} else if (dirstat->override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_RTL) {
buffy_set_prop(&(buf[bufoff]), BIDI_PROP_R);
}
if (dirstat->level + (dirstat->level % 2 == 0) + 1 <= MAX_DEPTH &&
overflow_isolate_count == 0 &&
overflow_embedding_count == 0) {
/* valid LRI */
valid_isolate_count++;
dirstat++;
dirstat->level = (dirstat - 1)->level + ((dirstat - 1)->level % 2 == 0) + 1;
dirstat->override = GRAPHEME_BIDIRECTIONAL_OVERRIDE_NEUTRAL;
dirstat->directional_isolate = true;
} else {
/* overflow LRI */
overflow_isolate_count++;
}
} else if (prop == BIDI_PROP_FSI) {
/* X5c */
if (get_paragraph_level(GRAPHEME_BIDIRECTIONAL_OVERRIDE_NEUTRAL, true,
buf + (bufoff + 1), buflen - (bufoff + 1)) == 1) {
prop = BIDI_PROP_RLI;
goto again;
} else { /* ... == 0 */
prop = BIDI_PROP_LRI;
goto again;
}
} else if (prop != BIDI_PROP_B &&
prop != BIDI_PROP_BN &&
prop != BIDI_PROP_PDF &&
prop != BIDI_PROP_PDI) {
/* X6 */
buffy_set_level(&(buf[bufoff]), dirstat->level);
if (dirstat->override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_LTR) {
buffy_set_prop(&(buf[bufoff]), BIDI_PROP_L);
} else if (dirstat->override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_RTL) {
buffy_set_prop(&(buf[bufoff]), BIDI_PROP_R);
}
} else if (prop == BIDI_PROP_PDI) {
/* X6a */
if (overflow_isolate_count > 0) {
/* PDI matches an overflow isolate initiator */
overflow_isolate_count--;
} else if (valid_isolate_count > 0) {
/* PDI matches a normal isolate initiator */
overflow_embedding_count = 0;
while (dirstat->directional_isolate == false &&
dirstat > directional_status) {
/*
* we are safe here as given the
* valid isolate count is positive
* there must be a stack-entry
* with positive directional
* isolate status, but we take
* no chances and include an
* explicit check
*
* POSSIBLE OPTIMIZATION: Whenever
* we push on the stack, check if it
* has the directional isolate status
* true and store a pointer to it
* so we can jump to it very quickly.
*/
dirstat--;
}
/*
* as above, the following check is not
* necessary, given we are guaranteed to
* have at least one stack entry left,
* but it's better to be safe
*/
if (dirstat > directional_status) {
dirstat--;
}
valid_isolate_count--;
}
buffy_set_level(&(buf[bufoff]), dirstat->level);
if (dirstat->override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_LTR) {
buffy_set_prop(&(buf[bufoff]), BIDI_PROP_L);
} else if (dirstat->override == GRAPHEME_BIDIRECTIONAL_OVERRIDE_RTL) {
buffy_set_prop(&(buf[bufoff]), BIDI_PROP_R);
}
} else if (prop == BIDI_PROP_PDF) {
/* X7 */
if (overflow_isolate_count > 0) {
/* do nothing */
} else if (overflow_embedding_count > 0) {
overflow_embedding_count--;
} else if (dirstat->directional_isolate == false &&
dirstat > directional_status) {
dirstat--;
}
} else if (prop == BIDI_PROP_B) {
/* X8 */
buffy_set_level(&(buf[bufoff]), paragraph_level);
}
/* X9 */
if (prop == BIDI_PROP_RLE ||
prop == BIDI_PROP_LRE ||
prop == BIDI_PROP_RLO ||
prop == BIDI_PROP_LRO ||
prop == BIDI_PROP_PDF ||
prop == BIDI_PROP_BN) {
buffy_set_level(&(buf[bufoff]), -1);
}
}
/* X10 (W1-W7, N0-N2) */
for (bufoff = 0; bufoff < buflen; bufoff++) {
if (!buffy_is_visited(buf[bufoff]) && buffy_get_level(buf[bufoff]) != -1) {
bufoff += process_isolating_run_sequence(buf, buflen, bufoff,
paragraph_level);
}
}
/*
* I1-I2 (given our sequential approach to processing the
* isolating run sequences, we apply this rule separately)
*/
for (bufoff = 0; bufoff < buflen; bufoff++) {
prop = buffy_get_prop(buf[bufoff]);
level = buffy_get_level(buf[bufoff]);
if (level % 2 == 0 ) {
/* even level */
if (prop == BIDI_PROP_R) {
level += 1;
} else if (prop == BIDI_PROP_AN ||
prop == BIDI_PROP_EN) {
level += 2;
}
} else {
/* odd level */
if (prop == BIDI_PROP_L ||
prop == BIDI_PROP_EN ||
prop == BIDI_PROP_AN) {
level += 1;
}
}
buffy_set_level(&(buf[bufoff]), level);
}
/* L1 (rules 1-3) */
runsince = SIZE_MAX;
for (bufoff = 0; bufoff < buflen; bufoff++) {
rawprop = buffy_get_rawprop(buf[bufoff]);
level = buffy_get_level(buf[bufoff]);
if (level == -1) {
/* ignored character */
continue;
}
if (rawprop == BIDI_PROP_WS ||
rawprop == BIDI_PROP_FSI ||
rawprop == BIDI_PROP_LRI ||
rawprop == BIDI_PROP_RLI ||
rawprop == BIDI_PROP_PDI) {
if (runsince == SIZE_MAX) {
/* a new run has begun */
runsince = bufoff;
}
} else if (rawprop == BIDI_PROP_S ||
rawprop == BIDI_PROP_B) {
/* L1.4 -- ignored for now, < beachten! */
for (i = runsince; i < bufoff; i++) {
if (buffy_get_level(buf[i]) != -1) {
buffy_set_level(&(buf[i]), paragraph_level);
}
}
runsince = SIZE_MAX;
} else {
/* sequence ended */
runsince = SIZE_MAX;
}
if (rawprop == BIDI_PROP_S ||
rawprop == BIDI_PROP_B) {
buffy_set_level(&(buf[bufoff]), paragraph_level);
}
continue;
}
if (runsince != SIZE_MAX) {
/*
* this is the end of the line and we
* are in a run
*/
for (i = runsince; i < buflen; i++) {
if (buffy_get_level(buf[i]) != -1) {
buffy_set_level(&(buf[i]), paragraph_level);
}
}
runsince = SIZE_MAX;
}
}
static size_t
get_embedding_levels(HERODOTUS_READER *r, enum grapheme_bidirectional_override override,
int_least32_t *buf, size_t buflen)
{
size_t bufoff, bufsize, lastparoff;
uint_least32_t cp;
if (buf == NULL) {
for (; herodotus_read_codepoint(r, true, &cp) ==
HERODOTUS_STATUS_SUCCESS;)
;
/* see below for return value reasoning */
return herodotus_reader_number_read(r);
}
/*
* the first step is to determine the bidirectional properties
* and store them in the buffer
*/
for (bufoff = 0; herodotus_read_codepoint(r, true, &cp) ==
HERODOTUS_STATUS_SUCCESS; bufoff++) {
if (bufoff < buflen) {
/*
* actually only do something when we have
* space in the level-buffer. We continue
* the iteration to be able to give a good
* return value
*/
buf[bufoff] = 0;
buffy_set_prop(&(buf[bufoff]), get_bidi_property(cp));
buffy_set_rawprop(&(buf[bufoff]), get_bidi_property(cp));
}
}
bufsize = herodotus_reader_number_read(r);
for (bufoff = 0, lastparoff = 0; bufoff < bufsize; bufoff++) {
if (buffy_get_prop(buf[bufoff]) != BIDI_PROP_B &&
bufoff != bufsize - 1) {
continue;
}
/*
* we either encountered a paragraph terminator or this
* is the last character in the string.
* Call the paragraph handler on the paragraph, including
* the terminating character or last character of the
* string respectively
*/
get_paragraph_embedding_levels(override, buf + lastparoff,
bufoff + 1 - lastparoff);
lastparoff = bufoff + 1;
}
/* bake the levels into the buffer, discarding the metadata */
for (bufoff = 0; bufoff < bufsize; bufoff++) {
buf[bufoff] = buffy_get_level(buf[bufoff]);
}
/*
* we return the number of total bytes read, as the function
* should indicate if the given level-buffer is too small
*/
return herodotus_reader_number_read(r);
}
size_t
grapheme_get_bidirectional_embedding_levels(const uint_least32_t *src, size_t srclen,
enum grapheme_bidirectional_override override,
int_least32_t *dest, size_t destlen)
{
HERODOTUS_READER r;
herodotus_reader_init(&r, HERODOTUS_TYPE_CODEPOINT, src, srclen);
return get_embedding_levels(&r, override, dest, destlen);
}
size_t
grapheme_get_bidirectional_embedding_levels_utf8(const char *src, size_t srclen,
enum grapheme_bidirectional_override override,
int_least32_t *dest, size_t destlen)
{
HERODOTUS_READER r;
herodotus_reader_init(&r, HERODOTUS_TYPE_UTF8, src, srclen);
return get_embedding_levels(&r, override, dest, destlen);
}