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