[coretext] Fix buffer resize handling

We can't really resize buffer and continue in this shaper as we are
using the scratch buffer for string_ref and log_cluster.  Restructure
shaper to retry from (almost) scratch.
This commit is contained in:
Behdad Esfahbod 2014-08-11 15:08:19 -04:00
parent 9b3c60c88b
commit a6b8dc8742
2 changed files with 256 additions and 211 deletions

View File

@ -183,6 +183,9 @@ struct hb_buffer_t {
inline bool ensure (unsigned int size)
{ return likely (!size || size < allocated) ? true : enlarge (size); }
inline bool ensure_inplace (unsigned int size)
{ return likely (!size || size < allocated); }
HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
HB_INTERNAL bool shift_forward (unsigned int count);

View File

@ -428,12 +428,13 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
hb_auto_array_t<feature_record_t> feature_records;
hb_auto_array_t<range_record_t> range_records;
/*
* Set up features.
* (copied + modified from code from hb-uniscribe.cc)
*/
hb_auto_array_t<feature_record_t> feature_records;
hb_auto_array_t<range_record_t> range_records;
if (num_features)
{
/* Sort features by start/end events. */
@ -576,32 +577,26 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
num_features = 0;
}
#define FAIL(...) \
HB_STMT_START { \
DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
return false; \
} HB_STMT_END;
unsigned int scratch_size;
hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
#define ALLOCATE_ARRAY(Type, name, len) \
#define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
Type *name = (Type *) scratch; \
{ \
unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
assert (_consumed <= scratch_size); \
if (unlikely (_consumed > scratch_size)) \
{ \
on_no_room; \
assert (0); \
} \
scratch += _consumed; \
scratch_size -= _consumed; \
}
#define utf16_index() var1.u32
ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2);
ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/);
unsigned int chars_len = 0;
for (unsigned int i = 0; i < buffer->len; i++) {
hb_codepoint_t c = buffer->info[i].codepoint;
buffer->info[i].utf16_index() = chars_len;
if (likely (c <= 0xFFFFu))
pchars[chars_len++] = c;
else if (unlikely (c > 0x10FFFFu))
@ -612,9 +607,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
}
}
#undef utf16_index
ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len);
ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/);
chars_len = 0;
for (unsigned int i = 0; i < buffer->len; i++)
{
@ -625,11 +618,52 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
log_clusters[chars_len++] = cluster; /* Surrogates. */
}
CFStringRef string_ref = CFStringCreateWithCharactersNoCopy (NULL,
#define FAIL(...) \
HB_STMT_START { \
DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
ret = false; \
goto fail; \
} HB_STMT_END;
bool ret = true;
CFStringRef string_ref = NULL;
CTLineRef line = NULL;
if (0)
{
resize_and_retry:
printf ("HERE");
/* string_ref uses the scratch-buffer for backing store, and line references
* string_ref (via attr_string). We must release those before resizing buffer. */
assert (string_ref);
assert (line);
CFRelease (string_ref);
CFRelease (line);
string_ref = NULL;
line = NULL;
if (unlikely (!buffer->ensure (buffer->allocated * 2)))
FAIL ("Buffer resize failed");
/* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the cleanest way to do without
* completely restructuring the rest of this shaper. */
hb_buffer_t::scratch_buffer_t *old_scratch = scratch;
scratch = buffer->get_scratch_buffer (&scratch_size);
pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch)));
log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch)));
}
retry:
{
string_ref = CFStringCreateWithCharactersNoCopy (NULL,
pchars, chars_len,
kCFAllocatorNull);
if (unlikely (!string_ref))
FAIL ("CFStringCreateWithCharactersNoCopy failed");
/* Create an attributed string, populate it, and create a line from it, then release attributed string. */
{
CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (NULL, chars_len);
if (unlikely (!attr_string))
FAIL ("CFAttributedStringCreateMutable failed");
CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
kCTFontAttributeName, font_data->ct_font);
@ -661,8 +695,12 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
kCTFontAttributeName, last_range->font);
}
CTLineRef line = CTLineCreateWithAttributedString (attr_string);
line = CTLineCreateWithAttributedString (attr_string);
CFRelease (attr_string);
}
if (unlikely (!line))
FAIL ("CFLineCreateWithAttributedString failed");
CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
unsigned int num_runs = CFArrayGetCount (glyph_runs);
@ -700,9 +738,8 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
if (!matched)
{
CFRange range = CTRunGetStringRange (run);
buffer->ensure (buffer->len + range.length);
if (unlikely (buffer->in_error))
FAIL ("Buffer resize failed");
if (!buffer->ensure_inplace (buffer->len + range.length))
goto resize_and_retry;
hb_glyph_info_t *info = buffer->info + buffer->len;
CGGlyph notdef = 0;
@ -740,38 +777,39 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
if (num_glyphs == 0)
continue;
/* Needed buffer size in case we end up using scratch buffer. */
unsigned int alt_size = (sizeof (CGGlyph) + sizeof (CGPoint) + sizeof (CFIndex)) / sizeof (hb_glyph_info_t) + 2;
buffer->ensure (MAX (buffer->len + num_glyphs, alt_size));
if (unlikely (buffer->in_error))
FAIL ("Buffer resize failed");
if (!buffer->ensure (MAX (buffer->len + num_glyphs, alt_size)))
goto resize_and_retry;
scratch = buffer->get_scratch_buffer (&scratch_size);
/* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
* succeed, and so copying data to our own buffer will be rare. Reports
* have it that this changed in OS X 10.10 Yosemite, and NULL is returned
* frequently. At any rate, we can test that codepath by setting USE_PTR
* to false. */
#define USE_PTR true
/* Testing indicates that CTRunGetGlyphsPtr, etc (almost?) always
* succeed, and so copying data to our own buffer will be rare. */
const CGGlyph* glyphs = CTRunGetGlyphsPtr (run);
const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL;
if (!glyphs) {
ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs);
ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry);
CTRunGetGlyphs (run, range_all, glyph_buf);
glyphs = glyph_buf;
}
const CGPoint* positions = CTRunGetPositionsPtr (run);
const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL;
if (!positions) {
ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs);
ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry);
CTRunGetPositions (run, range_all, position_buf);
positions = position_buf;
}
const CFIndex* string_indices = CTRunGetStringIndicesPtr (run);
const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL;
if (!string_indices) {
ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs);
ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry);
CTRunGetStringIndices (run, range_all, index_buf);
string_indices = index_buf;
}
#undef USE_PTR
#undef ALLOCATE_ARRAY
double run_width = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
@ -793,10 +831,6 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
}
}
for (unsigned int i = 0; i < range_records.len; i++)
if (range_records[i].font)
CFRelease (range_records[i].font);
buffer->clear_positions ();
unsigned int count = buffer->len;
@ -839,11 +873,19 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
}
}
}
}
fail:
if (string_ref)
CFRelease (string_ref);
if (line)
CFRelease (line);
return true;
for (unsigned int i = 0; i < range_records.len; i++)
if (range_records[i].font)
CFRelease (range_records[i].font);
return ret;
}