xref: /aosp_15_r20/external/harfbuzz_ng/src/graph/serialize.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
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