1*2d1272b8SAndroid Build Coastguard Worker /*
2*2d1272b8SAndroid Build Coastguard Worker * Copyright © 2022 Google, Inc.
3*2d1272b8SAndroid Build Coastguard Worker *
4*2d1272b8SAndroid Build Coastguard Worker * This is part of HarfBuzz, a text shaping library.
5*2d1272b8SAndroid Build Coastguard Worker *
6*2d1272b8SAndroid Build Coastguard Worker * Permission is hereby granted, without written agreement and without
7*2d1272b8SAndroid Build Coastguard Worker * license or royalty fees, to use, copy, modify, and distribute this
8*2d1272b8SAndroid Build Coastguard Worker * software and its documentation for any purpose, provided that the
9*2d1272b8SAndroid Build Coastguard Worker * above copyright notice and the following two paragraphs appear in
10*2d1272b8SAndroid Build Coastguard Worker * all copies of this software.
11*2d1272b8SAndroid Build Coastguard Worker *
12*2d1272b8SAndroid Build Coastguard Worker * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13*2d1272b8SAndroid Build Coastguard Worker * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14*2d1272b8SAndroid Build Coastguard Worker * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15*2d1272b8SAndroid Build Coastguard Worker * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16*2d1272b8SAndroid Build Coastguard Worker * DAMAGE.
17*2d1272b8SAndroid Build Coastguard Worker *
18*2d1272b8SAndroid Build Coastguard Worker * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19*2d1272b8SAndroid Build Coastguard Worker * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20*2d1272b8SAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21*2d1272b8SAndroid Build Coastguard Worker * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22*2d1272b8SAndroid Build Coastguard Worker * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23*2d1272b8SAndroid Build Coastguard Worker *
24*2d1272b8SAndroid Build Coastguard Worker * Google Author(s): Garret Rieger
25*2d1272b8SAndroid Build Coastguard Worker */
26*2d1272b8SAndroid Build Coastguard Worker
27*2d1272b8SAndroid Build Coastguard Worker #ifndef GRAPH_SERIALIZE_HH
28*2d1272b8SAndroid Build Coastguard Worker #define GRAPH_SERIALIZE_HH
29*2d1272b8SAndroid Build Coastguard Worker
30*2d1272b8SAndroid Build Coastguard Worker namespace graph {
31*2d1272b8SAndroid Build Coastguard Worker
32*2d1272b8SAndroid Build Coastguard Worker struct overflow_record_t
33*2d1272b8SAndroid Build Coastguard Worker {
34*2d1272b8SAndroid Build Coastguard Worker unsigned parent;
35*2d1272b8SAndroid Build Coastguard Worker unsigned child;
36*2d1272b8SAndroid Build Coastguard Worker
operator !=graph::overflow_record_t37*2d1272b8SAndroid Build Coastguard Worker bool operator != (const overflow_record_t o) const
38*2d1272b8SAndroid Build Coastguard Worker { return !(*this == o); }
39*2d1272b8SAndroid Build Coastguard Worker
operator ==graph::overflow_record_t40*2d1272b8SAndroid Build Coastguard Worker inline bool operator == (const overflow_record_t& o) const
41*2d1272b8SAndroid Build Coastguard Worker {
42*2d1272b8SAndroid Build Coastguard Worker return parent == o.parent &&
43*2d1272b8SAndroid Build Coastguard Worker child == o.child;
44*2d1272b8SAndroid Build Coastguard Worker }
45*2d1272b8SAndroid Build Coastguard Worker
hashgraph::overflow_record_t46*2d1272b8SAndroid Build Coastguard Worker inline uint32_t hash () const
47*2d1272b8SAndroid Build Coastguard Worker {
48*2d1272b8SAndroid Build Coastguard Worker uint32_t current = 0;
49*2d1272b8SAndroid Build Coastguard Worker current = current * 31 + hb_hash (parent);
50*2d1272b8SAndroid Build Coastguard Worker current = current * 31 + hb_hash (child);
51*2d1272b8SAndroid Build Coastguard Worker return current;
52*2d1272b8SAndroid Build Coastguard Worker }
53*2d1272b8SAndroid Build Coastguard Worker };
54*2d1272b8SAndroid Build Coastguard Worker
55*2d1272b8SAndroid Build Coastguard Worker inline
compute_offset(const graph_t & graph,unsigned parent_idx,const hb_serialize_context_t::object_t::link_t & link)56*2d1272b8SAndroid Build Coastguard Worker int64_t compute_offset (
57*2d1272b8SAndroid Build Coastguard Worker const graph_t& graph,
58*2d1272b8SAndroid Build Coastguard Worker unsigned parent_idx,
59*2d1272b8SAndroid Build Coastguard Worker const hb_serialize_context_t::object_t::link_t& link)
60*2d1272b8SAndroid Build Coastguard Worker {
61*2d1272b8SAndroid Build Coastguard Worker const auto& parent = graph.vertices_[parent_idx];
62*2d1272b8SAndroid Build Coastguard Worker const auto& child = graph.vertices_[link.objidx];
63*2d1272b8SAndroid Build Coastguard Worker int64_t offset = 0;
64*2d1272b8SAndroid Build Coastguard Worker switch ((hb_serialize_context_t::whence_t) link.whence) {
65*2d1272b8SAndroid Build Coastguard Worker case hb_serialize_context_t::whence_t::Head:
66*2d1272b8SAndroid Build Coastguard Worker offset = child.start - parent.start; break;
67*2d1272b8SAndroid Build Coastguard Worker case hb_serialize_context_t::whence_t::Tail:
68*2d1272b8SAndroid Build Coastguard Worker offset = child.start - parent.end; break;
69*2d1272b8SAndroid Build Coastguard Worker case hb_serialize_context_t::whence_t::Absolute:
70*2d1272b8SAndroid Build Coastguard Worker offset = child.start; break;
71*2d1272b8SAndroid Build Coastguard Worker }
72*2d1272b8SAndroid Build Coastguard Worker
73*2d1272b8SAndroid Build Coastguard Worker assert (offset >= link.bias);
74*2d1272b8SAndroid Build Coastguard Worker offset -= link.bias;
75*2d1272b8SAndroid Build Coastguard Worker return offset;
76*2d1272b8SAndroid Build Coastguard Worker }
77*2d1272b8SAndroid Build Coastguard Worker
78*2d1272b8SAndroid Build Coastguard Worker inline
is_valid_offset(int64_t offset,const hb_serialize_context_t::object_t::link_t & link)79*2d1272b8SAndroid Build Coastguard Worker bool is_valid_offset (int64_t offset,
80*2d1272b8SAndroid Build Coastguard Worker const hb_serialize_context_t::object_t::link_t& link)
81*2d1272b8SAndroid Build Coastguard Worker {
82*2d1272b8SAndroid Build Coastguard Worker if (unlikely (!link.width))
83*2d1272b8SAndroid Build Coastguard Worker // Virtual links can't overflow.
84*2d1272b8SAndroid Build Coastguard Worker return link.is_signed || offset >= 0;
85*2d1272b8SAndroid Build Coastguard Worker
86*2d1272b8SAndroid Build Coastguard Worker if (link.is_signed)
87*2d1272b8SAndroid Build Coastguard Worker {
88*2d1272b8SAndroid Build Coastguard Worker if (link.width == 4)
89*2d1272b8SAndroid Build Coastguard Worker return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31);
90*2d1272b8SAndroid Build Coastguard Worker else
91*2d1272b8SAndroid Build Coastguard Worker return offset >= -(1 << 15) && offset < (1 << 15);
92*2d1272b8SAndroid Build Coastguard Worker }
93*2d1272b8SAndroid Build Coastguard Worker else
94*2d1272b8SAndroid Build Coastguard Worker {
95*2d1272b8SAndroid Build Coastguard Worker if (link.width == 4)
96*2d1272b8SAndroid Build Coastguard Worker return offset >= 0 && offset < ((int64_t) 1 << 32);
97*2d1272b8SAndroid Build Coastguard Worker else if (link.width == 3)
98*2d1272b8SAndroid Build Coastguard Worker return offset >= 0 && offset < ((int32_t) 1 << 24);
99*2d1272b8SAndroid Build Coastguard Worker else
100*2d1272b8SAndroid Build Coastguard Worker return offset >= 0 && offset < (1 << 16);
101*2d1272b8SAndroid Build Coastguard Worker }
102*2d1272b8SAndroid Build Coastguard Worker }
103*2d1272b8SAndroid Build Coastguard Worker
104*2d1272b8SAndroid Build Coastguard Worker /*
105*2d1272b8SAndroid Build Coastguard Worker * Will any offsets overflow on graph when it's serialized?
106*2d1272b8SAndroid Build Coastguard Worker */
107*2d1272b8SAndroid Build Coastguard Worker inline bool
will_overflow(graph_t & graph,hb_vector_t<overflow_record_t> * overflows=nullptr)108*2d1272b8SAndroid Build Coastguard Worker will_overflow (graph_t& graph,
109*2d1272b8SAndroid Build Coastguard Worker hb_vector_t<overflow_record_t>* overflows = nullptr)
110*2d1272b8SAndroid Build Coastguard Worker {
111*2d1272b8SAndroid Build Coastguard Worker if (overflows) overflows->resize (0);
112*2d1272b8SAndroid Build Coastguard Worker graph.update_positions ();
113*2d1272b8SAndroid Build Coastguard Worker
114*2d1272b8SAndroid Build Coastguard Worker hb_hashmap_t<overflow_record_t*, bool> record_set;
115*2d1272b8SAndroid Build Coastguard Worker const auto& vertices = graph.vertices_;
116*2d1272b8SAndroid Build Coastguard Worker for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--)
117*2d1272b8SAndroid Build Coastguard Worker {
118*2d1272b8SAndroid Build Coastguard Worker // Don't need to check virtual links for overflow
119*2d1272b8SAndroid Build Coastguard Worker for (const auto& link : vertices.arrayZ[parent_idx].obj.real_links)
120*2d1272b8SAndroid Build Coastguard Worker {
121*2d1272b8SAndroid Build Coastguard Worker int64_t offset = compute_offset (graph, parent_idx, link);
122*2d1272b8SAndroid Build Coastguard Worker if (likely (is_valid_offset (offset, link)))
123*2d1272b8SAndroid Build Coastguard Worker continue;
124*2d1272b8SAndroid Build Coastguard Worker
125*2d1272b8SAndroid Build Coastguard Worker if (!overflows) return true;
126*2d1272b8SAndroid Build Coastguard Worker
127*2d1272b8SAndroid Build Coastguard Worker overflow_record_t r;
128*2d1272b8SAndroid Build Coastguard Worker r.parent = parent_idx;
129*2d1272b8SAndroid Build Coastguard Worker r.child = link.objidx;
130*2d1272b8SAndroid Build Coastguard Worker if (record_set.has(&r)) continue; // don't keep duplicate overflows.
131*2d1272b8SAndroid Build Coastguard Worker
132*2d1272b8SAndroid Build Coastguard Worker overflows->push (r);
133*2d1272b8SAndroid Build Coastguard Worker record_set.set(&r, true);
134*2d1272b8SAndroid Build Coastguard Worker }
135*2d1272b8SAndroid Build Coastguard Worker }
136*2d1272b8SAndroid Build Coastguard Worker
137*2d1272b8SAndroid Build Coastguard Worker if (!overflows) return false;
138*2d1272b8SAndroid Build Coastguard Worker return overflows->length;
139*2d1272b8SAndroid Build Coastguard Worker }
140*2d1272b8SAndroid Build Coastguard Worker
141*2d1272b8SAndroid Build Coastguard Worker inline
print_overflows(graph_t & graph,const hb_vector_t<overflow_record_t> & overflows)142*2d1272b8SAndroid Build Coastguard Worker void print_overflows (graph_t& graph,
143*2d1272b8SAndroid Build Coastguard Worker const hb_vector_t<overflow_record_t>& overflows)
144*2d1272b8SAndroid Build Coastguard Worker {
145*2d1272b8SAndroid Build Coastguard Worker if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
146*2d1272b8SAndroid Build Coastguard Worker
147*2d1272b8SAndroid Build Coastguard Worker graph.update_parents ();
148*2d1272b8SAndroid Build Coastguard Worker int limit = 10;
149*2d1272b8SAndroid Build Coastguard Worker for (const auto& o : overflows)
150*2d1272b8SAndroid Build Coastguard Worker {
151*2d1272b8SAndroid Build Coastguard Worker if (!limit--) break;
152*2d1272b8SAndroid Build Coastguard Worker const auto& parent = graph.vertices_[o.parent];
153*2d1272b8SAndroid Build Coastguard Worker const auto& child = graph.vertices_[o.child];
154*2d1272b8SAndroid Build Coastguard Worker DEBUG_MSG (SUBSET_REPACK, nullptr,
155*2d1272b8SAndroid Build Coastguard Worker " overflow from "
156*2d1272b8SAndroid Build Coastguard Worker "%4u (%4u in, %4u out, space %2u) => "
157*2d1272b8SAndroid Build Coastguard Worker "%4u (%4u in, %4u out, space %2u)",
158*2d1272b8SAndroid Build Coastguard Worker o.parent,
159*2d1272b8SAndroid Build Coastguard Worker parent.incoming_edges (),
160*2d1272b8SAndroid Build Coastguard Worker parent.obj.real_links.length + parent.obj.virtual_links.length,
161*2d1272b8SAndroid Build Coastguard Worker graph.space_for (o.parent),
162*2d1272b8SAndroid Build Coastguard Worker o.child,
163*2d1272b8SAndroid Build Coastguard Worker child.incoming_edges (),
164*2d1272b8SAndroid Build Coastguard Worker child.obj.real_links.length + child.obj.virtual_links.length,
165*2d1272b8SAndroid Build Coastguard Worker graph.space_for (o.child));
166*2d1272b8SAndroid Build Coastguard Worker }
167*2d1272b8SAndroid Build Coastguard Worker if (overflows.length > 10) {
168*2d1272b8SAndroid Build Coastguard Worker DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %u more overflows.", overflows.length - 10);
169*2d1272b8SAndroid Build Coastguard Worker }
170*2d1272b8SAndroid Build Coastguard Worker }
171*2d1272b8SAndroid Build Coastguard Worker
172*2d1272b8SAndroid Build Coastguard Worker template <typename O> inline void
serialize_link_of_type(const hb_serialize_context_t::object_t::link_t & link,char * head,hb_serialize_context_t * c)173*2d1272b8SAndroid Build Coastguard Worker serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
174*2d1272b8SAndroid Build Coastguard Worker char* head,
175*2d1272b8SAndroid Build Coastguard Worker hb_serialize_context_t* c)
176*2d1272b8SAndroid Build Coastguard Worker {
177*2d1272b8SAndroid Build Coastguard Worker OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position);
178*2d1272b8SAndroid Build Coastguard Worker *offset = 0;
179*2d1272b8SAndroid Build Coastguard Worker c->add_link (*offset,
180*2d1272b8SAndroid Build Coastguard Worker // serializer has an extra nil object at the start of the
181*2d1272b8SAndroid Build Coastguard Worker // object array. So all id's are +1 of what our id's are.
182*2d1272b8SAndroid Build Coastguard Worker link.objidx + 1,
183*2d1272b8SAndroid Build Coastguard Worker (hb_serialize_context_t::whence_t) link.whence,
184*2d1272b8SAndroid Build Coastguard Worker link.bias);
185*2d1272b8SAndroid Build Coastguard Worker }
186*2d1272b8SAndroid Build Coastguard Worker
187*2d1272b8SAndroid Build Coastguard Worker inline
serialize_link(const hb_serialize_context_t::object_t::link_t & link,char * head,hb_serialize_context_t * c)188*2d1272b8SAndroid Build Coastguard Worker void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
189*2d1272b8SAndroid Build Coastguard Worker char* head,
190*2d1272b8SAndroid Build Coastguard Worker hb_serialize_context_t* c)
191*2d1272b8SAndroid Build Coastguard Worker {
192*2d1272b8SAndroid Build Coastguard Worker switch (link.width)
193*2d1272b8SAndroid Build Coastguard Worker {
194*2d1272b8SAndroid Build Coastguard Worker case 0:
195*2d1272b8SAndroid Build Coastguard Worker // Virtual links aren't serialized.
196*2d1272b8SAndroid Build Coastguard Worker return;
197*2d1272b8SAndroid Build Coastguard Worker case 4:
198*2d1272b8SAndroid Build Coastguard Worker if (link.is_signed)
199*2d1272b8SAndroid Build Coastguard Worker {
200*2d1272b8SAndroid Build Coastguard Worker serialize_link_of_type<OT::HBINT32> (link, head, c);
201*2d1272b8SAndroid Build Coastguard Worker } else {
202*2d1272b8SAndroid Build Coastguard Worker serialize_link_of_type<OT::HBUINT32> (link, head, c);
203*2d1272b8SAndroid Build Coastguard Worker }
204*2d1272b8SAndroid Build Coastguard Worker return;
205*2d1272b8SAndroid Build Coastguard Worker case 2:
206*2d1272b8SAndroid Build Coastguard Worker if (link.is_signed)
207*2d1272b8SAndroid Build Coastguard Worker {
208*2d1272b8SAndroid Build Coastguard Worker serialize_link_of_type<OT::HBINT16> (link, head, c);
209*2d1272b8SAndroid Build Coastguard Worker } else {
210*2d1272b8SAndroid Build Coastguard Worker serialize_link_of_type<OT::HBUINT16> (link, head, c);
211*2d1272b8SAndroid Build Coastguard Worker }
212*2d1272b8SAndroid Build Coastguard Worker return;
213*2d1272b8SAndroid Build Coastguard Worker case 3:
214*2d1272b8SAndroid Build Coastguard Worker serialize_link_of_type<OT::HBUINT24> (link, head, c);
215*2d1272b8SAndroid Build Coastguard Worker return;
216*2d1272b8SAndroid Build Coastguard Worker default:
217*2d1272b8SAndroid Build Coastguard Worker // Unexpected link width.
218*2d1272b8SAndroid Build Coastguard Worker assert (0);
219*2d1272b8SAndroid Build Coastguard Worker }
220*2d1272b8SAndroid Build Coastguard Worker }
221*2d1272b8SAndroid Build Coastguard Worker
222*2d1272b8SAndroid Build Coastguard Worker /*
223*2d1272b8SAndroid Build Coastguard Worker * serialize graph into the provided serialization buffer.
224*2d1272b8SAndroid Build Coastguard Worker */
serialize(const graph_t & graph)225*2d1272b8SAndroid Build Coastguard Worker inline hb_blob_t* serialize (const graph_t& graph)
226*2d1272b8SAndroid Build Coastguard Worker {
227*2d1272b8SAndroid Build Coastguard Worker hb_vector_t<char> buffer;
228*2d1272b8SAndroid Build Coastguard Worker size_t size = graph.total_size_in_bytes ();
229*2d1272b8SAndroid Build Coastguard Worker
230*2d1272b8SAndroid Build Coastguard Worker if (!size) return hb_blob_get_empty ();
231*2d1272b8SAndroid Build Coastguard Worker
232*2d1272b8SAndroid Build Coastguard Worker if (!buffer.alloc (size)) {
233*2d1272b8SAndroid Build Coastguard Worker DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
234*2d1272b8SAndroid Build Coastguard Worker return nullptr;
235*2d1272b8SAndroid Build Coastguard Worker }
236*2d1272b8SAndroid Build Coastguard Worker hb_serialize_context_t c((void *) buffer, size);
237*2d1272b8SAndroid Build Coastguard Worker
238*2d1272b8SAndroid Build Coastguard Worker c.start_serialize<void> ();
239*2d1272b8SAndroid Build Coastguard Worker const auto& vertices = graph.vertices_;
240*2d1272b8SAndroid Build Coastguard Worker for (unsigned i = 0; i < vertices.length; i++) {
241*2d1272b8SAndroid Build Coastguard Worker c.push ();
242*2d1272b8SAndroid Build Coastguard Worker
243*2d1272b8SAndroid Build Coastguard Worker size_t size = vertices[i].obj.tail - vertices[i].obj.head;
244*2d1272b8SAndroid Build Coastguard Worker char* start = c.allocate_size <char> (size);
245*2d1272b8SAndroid Build Coastguard Worker if (!start) {
246*2d1272b8SAndroid Build Coastguard Worker DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space.");
247*2d1272b8SAndroid Build Coastguard Worker return nullptr;
248*2d1272b8SAndroid Build Coastguard Worker }
249*2d1272b8SAndroid Build Coastguard Worker
250*2d1272b8SAndroid Build Coastguard Worker hb_memcpy (start, vertices[i].obj.head, size);
251*2d1272b8SAndroid Build Coastguard Worker
252*2d1272b8SAndroid Build Coastguard Worker // Only real links needs to be serialized.
253*2d1272b8SAndroid Build Coastguard Worker for (const auto& link : vertices[i].obj.real_links)
254*2d1272b8SAndroid Build Coastguard Worker serialize_link (link, start, &c);
255*2d1272b8SAndroid Build Coastguard Worker
256*2d1272b8SAndroid Build Coastguard Worker // All duplications are already encoded in the graph, so don't
257*2d1272b8SAndroid Build Coastguard Worker // enable sharing during packing.
258*2d1272b8SAndroid Build Coastguard Worker c.pop_pack (false);
259*2d1272b8SAndroid Build Coastguard Worker }
260*2d1272b8SAndroid Build Coastguard Worker c.end_serialize ();
261*2d1272b8SAndroid Build Coastguard Worker
262*2d1272b8SAndroid Build Coastguard Worker if (c.in_error ()) {
263*2d1272b8SAndroid Build Coastguard Worker DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d",
264*2d1272b8SAndroid Build Coastguard Worker c.errors);
265*2d1272b8SAndroid Build Coastguard Worker return nullptr;
266*2d1272b8SAndroid Build Coastguard Worker }
267*2d1272b8SAndroid Build Coastguard Worker
268*2d1272b8SAndroid Build Coastguard Worker return c.copy_blob ();
269*2d1272b8SAndroid Build Coastguard Worker }
270*2d1272b8SAndroid Build Coastguard Worker
271*2d1272b8SAndroid Build Coastguard Worker } // namespace graph
272*2d1272b8SAndroid Build Coastguard Worker
273*2d1272b8SAndroid Build Coastguard Worker #endif // GRAPH_SERIALIZE_HH
274