1/*
2 * Copyright (C) 1998-2004  David Turner and Werner Lemberg
3 * Copyright (C) 2004,2007  Red Hat, Inc.
4 *
5 * This is part of HarfBuzz, an OpenType Layout engine library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
26 */
27
28#include "harfbuzz-impl.h"
29#include "harfbuzz-buffer-private.h"
30#include "harfbuzz-gsub-private.h"
31#include "harfbuzz-gpos-private.h"
32
33/* Here is how the buffer works internally:
34 *
35 * There are two string pointers: in_string and out_string.  They
36 * always have same allocated size, but different length and positions.
37 *
38 * As an optimization, both in_string and out_string may point to the
39 * same piece of memory, which is owned by in_string.  This remains the
40 * case as long as:
41 *
42 *   - copy_glyph() is called
43 *   - replace_glyph() is called with inplace=TRUE
44 *   - add_output_glyph() and add_output_glyphs() are not called
45 *
46 * In that case swap(), and copy_glyph(), and replace_glyph() are all
47 * mostly no-op.
48 *
49 * As soon an add_output_glyph[s]() or replace_glyph() with inplace=FALSE is
50 * called, out_string is moved over to an alternate buffer (alt_string), and
51 * its current contents (out_length entries) are copied to the alt buffer.
52 * This should all remain transparent to the user.  swap() then switches
53 * in_string and alt_string.  alt_string is not allocated until its needed,
54 * but after that it's grown with in_string unconditionally.
55 *
56 * The buffer->separate_out boolean keeps status of whether out_string points
57 * to in_string (FALSE) or alt_string (TRUE).
58 */
59
60/* Internal API */
61
62static HB_Error
63hb_buffer_ensure( HB_Buffer buffer,
64		   HB_UInt   size )
65{
66  HB_UInt new_allocated = buffer->allocated;
67
68  if (size > new_allocated)
69    {
70      HB_Error error;
71
72      while (size > new_allocated)
73	new_allocated += (new_allocated >> 1) + 8;
74
75      if ( buffer->positions )
76        {
77	  if ( REALLOC_ARRAY( buffer->positions, new_allocated, HB_PositionRec ) )
78	    return error;
79	}
80
81      if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) )
82	return error;
83
84      if ( buffer->separate_out )
85        {
86	  if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
87	    return error;
88
89	  buffer->out_string = buffer->alt_string;
90	}
91      else
92        {
93	  buffer->out_string = buffer->in_string;
94
95	  if ( buffer->alt_string )
96	    {
97	      if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
98		return error;
99	    }
100	}
101
102      buffer->allocated = new_allocated;
103    }
104
105  return HB_Err_Ok;
106}
107
108static HB_Error
109hb_buffer_duplicate_out_buffer( HB_Buffer buffer )
110{
111  if ( !buffer->alt_string )
112    {
113      HB_Error error;
114
115      if ( ALLOC_ARRAY( buffer->alt_string, buffer->allocated, HB_GlyphItemRec ) )
116	return error;
117    }
118
119  buffer->out_string = buffer->alt_string;
120  memcpy( buffer->out_string, buffer->in_string, buffer->out_length * sizeof (buffer->out_string[0]) );
121  buffer->separate_out = TRUE;
122
123  return HB_Err_Ok;
124}
125
126/* Public API */
127
128HB_Error
129hb_buffer_new( HB_Buffer *pbuffer )
130{
131  HB_Buffer buffer;
132  HB_Error error;
133
134  if ( ALLOC( buffer, sizeof( HB_BufferRec ) ) )
135    return error;
136
137  buffer->allocated = 0;
138  buffer->in_string = NULL;
139  buffer->alt_string = NULL;
140  buffer->positions = NULL;
141
142  hb_buffer_clear( buffer );
143
144  *pbuffer = buffer;
145
146  return HB_Err_Ok;
147}
148
149void
150hb_buffer_free( HB_Buffer buffer )
151{
152  FREE( buffer->in_string );
153  FREE( buffer->alt_string );
154  buffer->out_string = NULL;
155  FREE( buffer->positions );
156  FREE( buffer );
157}
158
159void
160hb_buffer_clear( HB_Buffer buffer )
161{
162  buffer->in_length = 0;
163  buffer->out_length = 0;
164  buffer->in_pos = 0;
165  buffer->out_pos = 0;
166  buffer->out_string = buffer->in_string;
167  buffer->separate_out = FALSE;
168  buffer->max_ligID = 0;
169}
170
171HB_Error
172hb_buffer_add_glyph( HB_Buffer buffer,
173		      HB_UInt   glyph_index,
174		      HB_UInt   properties,
175		      HB_UInt   cluster )
176{
177  HB_Error error;
178  HB_GlyphItem glyph;
179
180  error = hb_buffer_ensure( buffer, buffer->in_length + 1 );
181  if ( error )
182    return error;
183
184  glyph = &buffer->in_string[buffer->in_length];
185  glyph->gindex = glyph_index;
186  glyph->properties = properties;
187  glyph->cluster = cluster;
188  glyph->component = 0;
189  glyph->ligID = 0;
190  glyph->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
191
192  buffer->in_length++;
193
194  return HB_Err_Ok;
195}
196
197/* HarfBuzz-Internal API */
198
199HB_INTERNAL void
200_hb_buffer_clear_output( HB_Buffer buffer )
201{
202  buffer->out_length = 0;
203  buffer->out_pos = 0;
204  buffer->out_string = buffer->in_string;
205  buffer->separate_out = FALSE;
206}
207
208HB_INTERNAL HB_Error
209_hb_buffer_clear_positions( HB_Buffer buffer )
210{
211  if ( !buffer->positions )
212    {
213      HB_Error error;
214
215      if ( ALLOC_ARRAY( buffer->positions, buffer->allocated, HB_PositionRec ) )
216	return error;
217    }
218
219  memset (buffer->positions, 0, sizeof (buffer->positions[0]) * buffer->in_length);
220
221  return HB_Err_Ok;
222}
223
224HB_INTERNAL void
225_hb_buffer_swap( HB_Buffer buffer )
226{
227  HB_GlyphItem tmp_string;
228  int tmp_length;
229  int tmp_pos;
230
231  if ( buffer->separate_out )
232    {
233      tmp_string = buffer->in_string;
234      buffer->in_string = buffer->out_string;
235      buffer->out_string = tmp_string;
236      buffer->alt_string = buffer->out_string;
237    }
238
239  tmp_length = buffer->in_length;
240  buffer->in_length = buffer->out_length;
241  buffer->out_length = tmp_length;
242
243  tmp_pos = buffer->in_pos;
244  buffer->in_pos = buffer->out_pos;
245  buffer->out_pos = tmp_pos;
246}
247
248/* The following function copies `num_out' elements from `glyph_data'
249   to `buffer->out_string', advancing the in array pointer in the structure
250   by `num_in' elements, and the out array pointer by `num_out' elements.
251   Finally, it sets the `length' field of `out' equal to
252   `pos' of the `out' structure.
253
254   If `component' is 0xFFFF, the component value from buffer->in_pos
255   will copied `num_out' times, otherwise `component' itself will
256   be used to fill the `component' fields.
257
258   If `ligID' is 0xFFFF, the ligID value from buffer->in_pos
259   will copied `num_out' times, otherwise `ligID' itself will
260   be used to fill the `ligID' fields.
261
262   The properties for all replacement glyphs are taken
263   from the glyph at position `buffer->in_pos'.
264
265   The cluster value for the glyph at position buffer->in_pos is used
266   for all replacement glyphs */
267HB_INTERNAL HB_Error
268_hb_buffer_add_output_glyphs( HB_Buffer  buffer,
269			      HB_UShort  num_in,
270			      HB_UShort  num_out,
271			      HB_UShort *glyph_data,
272			      HB_UShort  component,
273			      HB_UShort  ligID )
274{
275  HB_Error  error;
276  HB_UShort i;
277  HB_UInt properties;
278  HB_UInt cluster;
279
280  error = hb_buffer_ensure( buffer, buffer->out_pos + num_out );
281  if ( error )
282    return error;
283
284  if ( !buffer->separate_out )
285    {
286      error = hb_buffer_duplicate_out_buffer( buffer );
287      if ( error )
288	return error;
289    }
290
291  properties = buffer->in_string[buffer->in_pos].properties;
292  cluster = buffer->in_string[buffer->in_pos].cluster;
293  if ( component == 0xFFFF )
294    component = buffer->in_string[buffer->in_pos].component;
295  if ( ligID == 0xFFFF )
296    ligID = buffer->in_string[buffer->in_pos].ligID;
297
298  for ( i = 0; i < num_out; i++ )
299  {
300    HB_GlyphItem item = &buffer->out_string[buffer->out_pos + i];
301
302    item->gindex = glyph_data[i];
303    item->properties = properties;
304    item->cluster = cluster;
305    item->component = component;
306    item->ligID = ligID;
307    item->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
308  }
309
310  buffer->in_pos  += num_in;
311  buffer->out_pos += num_out;
312
313  buffer->out_length = buffer->out_pos;
314
315  return HB_Err_Ok;
316}
317
318HB_INTERNAL HB_Error
319_hb_buffer_add_output_glyph( HB_Buffer buffer,
320			     HB_UInt   glyph_index,
321			     HB_UShort component,
322			     HB_UShort ligID )
323{
324  HB_UShort glyph_data =  glyph_index;
325
326  return _hb_buffer_add_output_glyphs ( buffer, 1, 1,
327					&glyph_data, component, ligID );
328}
329
330HB_INTERNAL HB_Error
331_hb_buffer_copy_output_glyph ( HB_Buffer buffer )
332{
333  HB_Error  error;
334
335  error = hb_buffer_ensure( buffer, buffer->out_pos + 1 );
336  if ( error )
337    return error;
338
339  if ( buffer->separate_out )
340    {
341      buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
342    }
343
344  buffer->in_pos++;
345  buffer->out_pos++;
346  buffer->out_length = buffer->out_pos;
347
348  return HB_Err_Ok;
349}
350
351HB_INTERNAL HB_Error
352_hb_buffer_replace_output_glyph( HB_Buffer buffer,
353				 HB_UInt   glyph_index,
354				 HB_Bool   inplace )
355{
356
357  HB_Error error;
358
359  if ( inplace )
360    {
361      error = _hb_buffer_copy_output_glyph ( buffer );
362      if ( error )
363	return error;
364
365      buffer->out_string[buffer->out_pos-1].gindex = glyph_index;
366    }
367  else
368    {
369      return _hb_buffer_add_output_glyph( buffer, glyph_index, 0xFFFF, 0xFFFF );
370    }
371
372  return HB_Err_Ok;
373}
374
375HB_INTERNAL HB_UShort
376_hb_buffer_allocate_ligid( HB_Buffer buffer )
377{
378  buffer->max_ligID++;
379  if (HB_UNLIKELY (buffer->max_ligID == 0))
380    buffer->max_ligID++;
381
382  return buffer->max_ligID;
383}
384