sylware / charfbuzz (public) (License: LGPLv3) (since 2019-09-09) (hash sha1)
partial C implementation of harfbuzz C API for roman scripts

/hb-buffer.c (a3b6deceb732205dfeb4b3798227732f4632ed8b) (11835 bytes) (mode 100644) (type blob)

// C99 port from c++ is protected by a GNU Lesser GPLv3
// Copyright © 2013 Sylvain BERTRAND <sylvain.bertrand@gmail.com>
//                                   <sylware@legeek.net>       
#include <string.h>
#include <stdlib.h>
#include <assert.h>

#include "hb.h"
#include "hb-private.h"
#include "hb-atomic-private.h"
#include "hb-buffer-private.h"
#include "hb-unicode-private.h"
#include "hb-utf-private.h"

hb_bool_t
hb_segment_properties_equal(const hb_segment_properties_t *a,
                            const hb_segment_properties_t *b)
{
  return a->direction == b->direction &&
         a->script    == b->script    &&
         a->language  == b->language  &&
         a->reserved1 == b->reserved1 &&
         a->reserved2 == b->reserved2;
}

unsigned
hb_buffer_get_length(hb_buffer_t *buffer)
{
  return buffer->len;
}

hb_glyph_info_t *
hb_buffer_get_glyph_infos(hb_buffer_t *buffer, unsigned *length)
{
  if (length)
    *length = buffer->len;
  return buffer->info;
}

hb_glyph_position_t *
hb_buffer_get_glyph_positions(hb_buffer_t *buffer, unsigned *length)
{
  if (!buffer->have_positions)
    hb_buffer_clear_positions(buffer);

  if (length)
    *length = buffer->len;
  return buffer->pos;
}

void
hb_buffer_set_unicode_funcs(hb_buffer_t *buffer,
                            hb_unicode_funcs_t *unicode_funcs)
{
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  if (!unicode_funcs)
    unicode_funcs = hb_unicode_funcs_get_default();

  hb_unicode_funcs_reference(unicode_funcs);
  hb_unicode_funcs_destroy(buffer->unicode);
  buffer->unicode = unicode_funcs;
}

static void
hb_buffer_reverse_range(hb_buffer_t *buffer,
                        unsigned start,
                        unsigned end)
{
  unsigned i, j;

  if (start == end - 1)
    return;

  for (i = start, j = end - 1; i < j; i++, j--) {
    hb_glyph_info_t t;

    t = buffer->info[i];
    buffer->info[i] = buffer->info[j];
    buffer->info[j] = t;
  }

  if (buffer->pos) {
    for (i = start, j = end - 1; i < j; i++, j--) {
      hb_glyph_position_t t;

      t = buffer->pos[i];
      buffer->pos[i] = buffer->pos[j];
      buffer->pos[j] = t;
    }
  }
}

void
hb_buffer_reverse(hb_buffer_t *buffer)
{
  if (!buffer->len)
    return;

  hb_buffer_reverse_range(buffer, 0, buffer->len);
}

void
hb_buffer_set_direction(hb_buffer_t *buffer,
                        hb_direction_t direction)

{
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  buffer->props.direction = direction;
}

void
hb_buffer_clear_positions(hb_buffer_t *buffer)
{
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  buffer->have_output = FALSE;
  buffer->have_positions = TRUE;

  buffer->out_len = 0;
  buffer->out_info = buffer->info;

  memset(buffer->pos, 0, sizeof(buffer->pos[0]) * buffer->len);
}

static hb_buffer_t hb_buffer_nil = {
  REF_CNT_INVALID_VAL,//ref_cnt
  &_hb_unicode_funcs_nil,//unicode
  HB_SEGMENT_PROPERTIES_DEFAULT,
  HB_BUFFER_FLAG_DEFAULT,

  HB_BUFFER_CONTENT_TYPE_INVALID,
  FALSE,//in_error
  FALSE,//have_output
  FALSE//have_positions

  //Zero is good enough for everything else.
};

hb_buffer_t *
hb_buffer_get_empty(void)
{
  return &hb_buffer_nil;
}

void
hb_buffer_clear(hb_buffer_t *buffer)
{
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
  buffer->props = default_props;
  buffer->flags = HB_BUFFER_FLAG_DEFAULT;

  buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
  buffer->in_error = FALSE;
  buffer->have_output = FALSE;
  buffer->have_positions = FALSE;

  buffer->idx = 0;
  buffer->len = 0;
  buffer->out_len = 0;
  buffer->out_info = buffer->info;

  buffer->serial = 0;
  memset(buffer->allocated_var_bytes, 0, sizeof(buffer->allocated_var_bytes));
  memset(buffer->allocated_var_owner, 0, sizeof(buffer->allocated_var_owner));

  memset(buffer->context, 0, sizeof(buffer->context));
  memset(buffer->context_len, 0, sizeof(buffer->context_len));
}

void
hb_buffer_reset(hb_buffer_t *buffer)
{
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  hb_unicode_funcs_destroy(buffer->unicode);
  buffer->unicode = hb_unicode_funcs_get_default();

  hb_buffer_clear(buffer);
}

void
hb_buffer_destroy(hb_buffer_t *buffer)
{
  if (!buffer)
    return;
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;
  hb_atomic_int32_add(&buffer->ref_cnt, -1);
  if (hb_atomic_int32_get(&buffer->ref_cnt) > 0)
    return;
  hb_atomic_int32_set(&buffer->ref_cnt, REF_CNT_INVALID_VAL);

  hb_unicode_funcs_destroy(buffer->unicode);

  free(buffer->info);
  free(buffer->pos);
  free(buffer);
}

hb_buffer_t *
hb_buffer_create(void)
{
  hb_buffer_t *buffer = calloc(1, sizeof(*buffer));
  if (!buffer)
    return hb_buffer_get_empty();

  hb_atomic_int32_set(&buffer->ref_cnt, 1);
  buffer->unicode = hb_unicode_funcs_get_empty();

  hb_buffer_reset(buffer);
  return buffer;
}

void
hb_buffer_set_script(hb_buffer_t *buffer,
                     hb_script_t  script)
{
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  buffer->props.script = script;
}

//Here is how the buffer works internally:
//
//There are two info pointers: info and out_info.  They always have
//the same allocated size, but different lengths.
//
//As an optimization, both info and out_info may point to the
//same piece of memory, which is owned by info.  This remains the
//case as long as out_len doesn't exceed i at any time.
//In that case, swap_buffers() is no-op and the glyph operations operate
//mostly in-place.
//
//As soon as out_info gets longer than info, out_info is moved over
//to an alternate buffer (which we reuse the pos buffer for!), and its
//current contents (out_len entries) are copied to the new place.
//This should all remain transparent to the user.  swap_buffers() then
//switches info and out_info.

static hb_bool_t
enlarge(hb_buffer_t *buffer, unsigned int size)
{
  if (buffer->in_error)
    return FALSE;

  unsigned int new_allocated = buffer->allocated;
  hb_glyph_position_t *new_pos = NULL;
  hb_glyph_info_t *new_info = NULL;
  hb_bool_t separate_out = buffer->out_info != buffer->info;

  if (hb_unsigned_int_mul_overflows(size, sizeof(buffer->info[0])))
    goto done;

  while (size >= new_allocated)
    new_allocated += (new_allocated >> 1) + 32;

  //ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0]));
  if (hb_unsigned_int_mul_overflows(new_allocated, sizeof(buffer->info[0])))
    goto done;

  new_pos = (hb_glyph_position_t*)realloc(buffer->pos, new_allocated
                                                      * sizeof(buffer->pos[0]));
  new_info = (hb_glyph_info_t*)realloc(buffer->info, new_allocated
                                                     * sizeof(buffer->info[0]));

done:
  if (!new_pos || !new_info)
    buffer->in_error = TRUE;

  if (new_pos)
    buffer->pos = new_pos;

  if (new_info)
    buffer->info = new_info;

  buffer->out_info = separate_out ? (hb_glyph_info_t*)buffer->pos
                                                                 : buffer->info;
  if (!buffer->in_error)
    buffer->allocated = new_allocated;
  return !buffer->in_error;
}

static hb_bool_t
ensure(hb_buffer_t *buffer, unsigned int size)
{
  return (size < buffer->allocated) ? TRUE : enlarge(buffer, size);
}

static void
clear_context(hb_buffer_t *buffer, unsigned int side)
{
  buffer->context_len[side] = 0;
}

static void
add(hb_buffer_t *buffer, hb_codepoint_t codepoint, unsigned int cluster)
{
  hb_glyph_info_t *glyph;

  if (!ensure(buffer, buffer->len + 1)) return;

  glyph = &buffer->info[buffer->len];

  memset(glyph, 0, sizeof(*glyph));
  glyph->codepoint = codepoint;
  glyph->mask = 1;
  glyph->cluster = cluster;

  buffer->len++;
}

struct utf {
  unsigned bytes_n;
  unsigned (*len)(void *text);
  void *(*ptr_offset)(void *text, unsigned offset);
  void *(*prev)(void *text, void *start, hb_codepoint_t *unicode);
  unsigned (*diff)(void *a, void *b);
  void *(*next)(void *text, void *end, hb_codepoint_t *unicode);
};

static struct utf utf8 = {
  sizeof(uint8_t),
  hb_utf_strlen_utf8,
  hb_utf_ptr_offset_utf8,
  hb_utf_prev_utf8,
  hb_utf_diff_utf8,
  hb_utf_next_utf8
};

static HB_UNUSED struct utf utf16 = {
  sizeof(uint16_t),
  hb_utf_strlen_utf16,
  hb_utf_ptr_offset_utf16,
  hb_utf_prev_utf16,
  hb_utf_diff_utf16,
  hb_utf_next_utf16
};

static HB_UNUSED struct utf utf32 = {
  sizeof(uint32_t),
  hb_utf_strlen_utf32,
  hb_utf_ptr_offset_utf32,
  hb_utf_prev_utf32,
  hb_utf_diff_utf32,
  hb_utf_next_utf32
};

//to unroll the original c++, could have used a macro
//ASSEMBLY:maybe worth to be unrolled to fine tuned assembly
static void
hb_buffer_add_utf(hb_buffer_t *buffer,
                  struct utf *utf,
                  void *text,
                  int text_length,
                  unsigned item_offset,
                  int item_length)
{
  assert(buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
      (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));

  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  if (text_length == -1)
    text_length = utf->len(text);

  if (item_length == -1)
    item_length = text_length - item_offset;

  ensure(buffer, buffer->len + item_length * utf->bytes_n / 4);

  //If buffer is empty and pre-context provided, install it.
  //This check is written this way, to make sure people can
  //provide pre-context in one add_utf() call, then provide
  //text in a follow-up call.  See:
  //
  //https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13
  if (!buffer->len && item_offset > 0) {
    //Add pre-context
    clear_context(buffer, 0);
    void *prev = utf->ptr_offset(text, item_offset);
    void *start = text;
    while (start < prev && buffer->context_len[0] < HB_BUFFER_CONTEXT_LENGTH) {
      hb_codepoint_t u;
      prev = utf->prev(prev, start, &u);
      buffer->context[0][buffer->context_len[0]++] = u;
    }
  }

  void *next = utf->ptr_offset(text, item_offset);
  void *end = utf->ptr_offset(next, item_length);
  while (next < end) {
    hb_codepoint_t u;
    void *old_next = next;
    next = utf->next(next, end, &u);
    add(buffer, u, utf->diff(old_next, text));
  }

  //Add post-context
  clear_context(buffer, 1);
  end = utf->ptr_offset(text, text_length);
  end = text + text_length;
  while (next < end && buffer->context_len[1] < HB_BUFFER_CONTEXT_LENGTH) {
    hb_codepoint_t u;
    next = utf->next(next, end, &u);
    buffer->context[1][buffer->context_len[1]++] = u;
  }

  buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
}

void
hb_buffer_add_utf8(hb_buffer_t *buffer,
                   const char *text,
                   int text_length,
                   unsigned int item_offset,
                   int item_length)
{
  hb_buffer_add_utf(buffer,
                    &utf8,
                    (void*)text,
                    text_length,
                    item_offset,
                    item_length);
}

void
hb_buffer_set_flags(hb_buffer_t *buffer,
                    hb_buffer_flags_t flags)
{
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  buffer->flags = flags;
}

void
hb_buffer_set_language(hb_buffer_t *buffer,
                       hb_language_t language)
{
  if (hb_atomic_int32_get(&buffer->ref_cnt) == REF_CNT_INVALID_VAL)
    return;

  buffer->props.language = language;
}

hb_direction_t
hb_buffer_get_direction(hb_buffer_t *buffer)
{
  return buffer->props.direction;
}

void
hb_buffer_add(hb_buffer_t *buffer,
	      hb_codepoint_t codepoint,
	      unsigned int cluster)
{
  add(buffer, codepoint, cluster);
  clear_context(buffer, 1);
}


Mode Type Size Ref File
100644 blob 200 1a0b33b52f3f0b8f3567e0fb7b411e88c4b3c2c9 ABBREVIATIONS
100644 blob 119 8bbabe4337eabadd3f5ed07192b85e02d50f5bad FIXME
100644 blob 100 8a85692a2fc36a478ef9662ba2db214375a79254 README
100644 blob 223 7f27bbbdef02188df78858a31e0c38bda3fe4443 harfbuzz.pc.in
100644 blob 1478 2931ccaca651b04a927605232dd3500f7a661dce hb-atomic-private.h
100644 blob 120 ee630543fb4ef3e9fa49f7362026407c0fd0a858 hb-blob-private.h
100644 blob 4983 607f12b39c30bbe160c4e3a6781b5a58b64de2fc hb-blob.c
100644 blob 3467 d3d0f41b11c3b100cdee91525924c23226acd0cf hb-blob.h
100644 blob 1299 045519300e0994a11484696fa5c533663e4338db hb-buffer-private.h
100644 blob 11835 a3b6deceb732205dfeb4b3798227732f4632ed8b hb-buffer.c
100644 blob 8424 87c4ce58e83744e68765567e4107533dae9d5b7f hb-buffer.h
100644 blob 3363 f5141f528597232bb1a0214b7f1469cc9af008b8 hb-common.c
100644 blob 12423 40c1887296421c6d63f32e479cd318d4c4d79d19 hb-common.h
100644 blob 1407 c4954fa1b2cadb133a419584de60fe16263f148b hb-coretext.h
100644 blob 1584 30ae4b1caf76dd03cc682eda7458728b5380ad6c hb-deprecated.h
100644 blob 1226 3ea9861f08c6fcc0422092c8bb38dd808b5c8757 hb-face-private.h
100644 blob 5452 4489a99ae3f4ebded37dab45f8cde0bf32cc43f1 hb-face.c
100644 blob 2815 f682c468de58095fb0a7c4cdd577b34a9cb78077 hb-face.h
100644 blob 3705 f027e1e59453b39ecb722e2c7ffd9126b579b1a5 hb-fallback-shape.c
100644 blob 3222 172e69a09927c17f177947f2d5f18318dd3f4545 hb-font-private.h
100644 blob 30726 ba271fab87b5d9d6534bf971604b6079eabfc9f5 hb-font.c
100644 blob 12957 7273db43edaf542ef9d6e969fa2d8408e7d5100e hb-font.h
100644 blob 2570 bb44c035ff48e7655a397e5b95872d75b92945ad hb-ft.c
100644 blob 1756 696251e143f31987eb599f8cb16db4f12ff8d901 hb-ft.h
100644 blob 4016 91eaa7075c5f9fe797de93912301d2e03d6b7248 hb-glib.c
100644 blob 1496 63a9d3366c58a0110881ad00d971830924b73a2a hb-glib.h
100644 blob 3185 4a88d569e8d3be4daa2eaf39192891426da46514 hb-gobject-structs.h
100644 blob 1334 ea1bd25df82d46a222436a1ad078d0fc95d5c303 hb-gobject.h
100644 blob 1441 3eae54acbd81dbf3e534c4ad2e42d57611f7024e hb-graphite2.h
100644 blob 1495 f2f35f0f2ce13b6b5c46453128e452ea9380eabf hb-icu.h
100644 blob 2180 7172c280ad7dbb708d6818d386bdbb6e852d80c8 hb-open-file-private.h
100644 blob 2659 c4079cec956598ccf9521c874408a618fe7c29dc hb-open-file.c
100644 blob 65 642c3d3d45fdc42619d000cc9f7b3c890bdc0190 hb-ot-face-private.h
100644 blob 2806 ecfd5b109b43f549366a61efff16daa92db87349 hb-ot-layout.c
100644 blob 9212 d2a314cad6cbf94ba04ff9eaf513a74714eec732 hb-ot-layout.h
100644 blob 753 42faaca3f7ebdafc1232c6810b5ea2124896a95b hb-ot-tag.c
100644 blob 1688 1bf12ab3c09b9aef612515871e587c7c9ca7e62b hb-ot-tag.h
100644 blob 1526 80739063991cf14c4b65cc9247f751226f7f7277 hb-ot.h
100644 blob 1528 ae4bc85a37900c9f54cb91986dadc216f620a358 hb-private.h
100644 blob 3544 bafdae96333f9c81a4b20a4435e6eadaa31a7d32 hb-set.h
100644 blob 1647 1a90aed635a86f8e1d7002ffa8c7d300a3dd8acf hb-shape-plan-private.h
100644 blob 15942 929804aae7b6e5271cef5e9f1d654dcce7a8ca95 hb-shape-plan.c
100644 blob 2747 8f54552f90ba504f6ba2e9cf01459621fc73f9b2 hb-shape-plan.h
100644 blob 1370 1ff445b5de783fb0aa041f34d75919614b21ad34 hb-shape.c
100644 blob 2232 10a35cb517e14c96670d71bbcff266fce23cad91 hb-shape.h
100644 blob 1129 78e3fe4e3529b39789c0763b7f8e1019b3d5a725 hb-shaper-private.h
100644 blob 2184 d1c8f9740fec1b4d19e0967f60391f1c5b17d155 hb-shaper.c
100644 blob 1261 86293900edd4d5fab1013bd0c32eeaad2abef41e hb-unicode-private.h
100644 blob 8084 462ddc1b9bcad35a3d4b3babb64772c93650e75c hb-unicode.c
100644 blob 14218 1c4e097b92d5ba628455fefe25b6f4697cc16f11 hb-unicode.h
100644 blob 1427 51887c8794bce4e37b35c4bb9c675baf55b63052 hb-uniscribe.h
100644 blob 5070 97eb7798f1dc725aad02cc651ffe859044647582 hb-utf-private.c
100644 blob 1423 636ce353798d395c780be99bb68350c3352f40a3 hb-utf-private.h
100644 blob 1849 43634f91ce8a10250c8279b19a1993843feb0f38 hb-version.h.in
100644 blob 1470 c5a938a3818c9aca6de00a961c1c8f73bb2c47db hb.h
100644 blob 7651 65c5ca88a67c30becee01c5a8816d964b03862f9 license.md
100755 blob 5635 fedeb110245a5460039167ccc6cec1abca82c271 make
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/sylware/charfbuzz

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/sylware/charfbuzz

Clone this repository using git:
git clone git://git.rocketgit.com/user/sylware/charfbuzz

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main