1/*
2 * Copyright 2011 Google Inc. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "sfntly/data/readable_font_data.h"
18
19#include <stdio.h>
20
21#include "sfntly/data/memory_byte_array.h"
22#include "sfntly/data/writable_font_data.h"
23#include "sfntly/port/exception_type.h"
24
25namespace sfntly {
26
27ReadableFontData::ReadableFontData(ByteArray* array)
28    : FontData(array),
29      checksum_set_(false),
30      checksum_(0) {
31}
32
33ReadableFontData::~ReadableFontData() {}
34
35// TODO(arthurhsu): re-investigate the memory model of this function.  It's
36//                  not too useful without copying, but it's not performance
37//                  savvy to do copying.
38CALLER_ATTACH
39ReadableFontData* ReadableFontData::CreateReadableFontData(ByteVector* b) {
40  assert(b);
41  ByteArrayPtr ba = new MemoryByteArray(b->size());
42  ba->Put(0, b);
43  ReadableFontDataPtr wfd = new ReadableFontData(ba);
44  return wfd.Detach();
45}
46
47int64_t ReadableFontData::Checksum() {
48  AutoLock lock(checksum_lock_);
49  if (!checksum_set_) {
50    ComputeChecksum();
51  }
52  return checksum_;
53}
54
55void ReadableFontData::SetCheckSumRanges(const IntegerList& ranges) {
56  checksum_range_ = ranges;
57  checksum_set_ = false;  // UNIMPLEMENTED: atomicity
58}
59
60int32_t ReadableFontData::ReadUByte(int32_t index) {
61  int32_t b = array_->Get(BoundOffset(index));
62#if !defined (SFNTLY_NO_EXCEPTION)
63  if (b < 0) {
64    throw IndexOutOfBoundException(
65        "Index attempted to be read from is out of bounds", index);
66  }
67#endif
68  return b;
69}
70
71int32_t ReadableFontData::ReadByte(int32_t index) {
72  int32_t b = array_->Get(BoundOffset(index));
73#if !defined (SFNTLY_NO_EXCEPTION)
74  if (b < 0) {
75    throw IndexOutOfBoundException(
76        "Index attempted to be read from is out of bounds", index);
77  }
78#endif
79  return (b << 24) >> 24;
80}
81
82int32_t ReadableFontData::ReadBytes(int32_t index,
83                                    byte_t* b,
84                                    int32_t offset,
85                                    int32_t length) {
86  return array_->Get(BoundOffset(index), b, offset, BoundLength(index, length));
87}
88
89int32_t ReadableFontData::ReadChar(int32_t index) {
90  return ReadUByte(index);
91}
92
93int32_t ReadableFontData::ReadUShort(int32_t index) {
94  return 0xffff & (ReadUByte(index) << 8 | ReadUByte(index + 1));
95}
96
97int32_t ReadableFontData::ReadShort(int32_t index) {
98  return ((ReadByte(index) << 8 | ReadUByte(index + 1)) << 16) >> 16;
99}
100
101int32_t ReadableFontData::ReadUInt24(int32_t index) {
102  return 0xffffff & (ReadUByte(index) << 16 |
103                     ReadUByte(index + 1) << 8 |
104                     ReadUByte(index + 2));
105}
106
107int64_t ReadableFontData::ReadULong(int32_t index) {
108  return 0xffffffffL & (ReadUByte(index) << 24 |
109                        ReadUByte(index + 1) << 16 |
110                        ReadUByte(index + 2) << 8 |
111                        ReadUByte(index + 3));
112}
113
114int32_t ReadableFontData::ReadULongAsInt(int32_t index) {
115  int64_t ulong = ReadULong(index);
116#if !defined (SFNTLY_NO_EXCEPTION)
117  if ((ulong & 0x80000000) == 0x80000000) {
118    throw ArithmeticException("Long value too large to fit into an integer.");
119  }
120#endif
121  return static_cast<int32_t>(ulong);
122}
123
124int64_t ReadableFontData::ReadULongLE(int32_t index) {
125  return 0xffffffffL & (ReadUByte(index) |
126                        ReadUByte(index + 1) << 8 |
127                        ReadUByte(index + 2) << 16 |
128                        ReadUByte(index + 3) << 24);
129}
130
131int32_t ReadableFontData::ReadLong(int32_t index) {
132  return ReadByte(index) << 24 |
133         ReadUByte(index + 1) << 16 |
134         ReadUByte(index + 2) << 8 |
135         ReadUByte(index + 3);
136}
137
138int32_t ReadableFontData::ReadFixed(int32_t index) {
139  return ReadLong(index);
140}
141
142int64_t ReadableFontData::ReadDateTimeAsLong(int32_t index) {
143  return (int64_t)ReadULong(index) << 32 | ReadULong(index + 4);
144}
145
146int32_t ReadableFontData::ReadFWord(int32_t index) {
147  return ReadShort(index);
148}
149
150int32_t ReadableFontData::ReadFUFWord(int32_t index) {
151  return ReadUShort(index);
152}
153
154int32_t ReadableFontData::CopyTo(OutputStream* os) {
155  return array_->CopyTo(os, BoundOffset(0), Length());
156}
157
158int32_t ReadableFontData::CopyTo(WritableFontData* wfd) {
159  return array_->CopyTo(wfd->BoundOffset(0),
160                        wfd->array_,
161                        BoundOffset(0),
162                        Length());
163}
164
165int32_t ReadableFontData::CopyTo(ByteArray* ba) {
166  return array_->CopyTo(ba, BoundOffset(0), Length());
167}
168
169int32_t ReadableFontData::SearchUShort(int32_t start_index,
170                                       int32_t start_offset,
171                                       int32_t end_index,
172                                       int32_t end_offset,
173                                       int32_t length,
174                                       int32_t key) {
175  int32_t location = 0;
176  int32_t bottom = 0;
177  int32_t top = length;
178  while (top != bottom) {
179    location = (top + bottom) / 2;
180    int32_t location_start = ReadUShort(start_index + location * start_offset);
181    if (key < location_start) {
182      // location is below current location
183      top = location;
184    } else {
185      // is key below the upper bound?
186      int32_t location_end = ReadUShort(end_index + location * end_offset);
187#if defined (SFNTLY_DEBUG_FONTDATA)
188      fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
189#endif
190      if (key <= location_end) {
191        return location;
192      } else {
193        // location is above the current location
194        bottom = location + 1;
195      }
196    }
197  }
198  return -1;
199}
200
201int32_t ReadableFontData::SearchUShort(int32_t start_index,
202                                       int32_t start_offset,
203                                       int32_t length,
204                                       int32_t key) {
205  int32_t location = 0;
206  int32_t bottom = 0;
207  int32_t top = length;
208  while (top != bottom) {
209    location = (top + bottom) / 2;
210    int32_t location_start = ReadUShort(start_index + location * start_offset);
211    if (key < location_start) {
212      // location is below current location
213      top = location;
214    } else if (key > location_start) {
215      // location is above current location
216      bottom = location + 1;
217    } else {
218      return location;
219    }
220  }
221  return -1;
222}
223
224int32_t ReadableFontData::SearchULong(int32_t start_index,
225                                      int32_t start_offset,
226                                      int32_t end_index,
227                                      int32_t end_offset,
228                                      int32_t length,
229                                      int32_t key) {
230  int32_t location = 0;
231  int32_t bottom = 0;
232  int32_t top = length;
233  while (top != bottom) {
234    location = (top + bottom) / 2;
235    int32_t location_start = ReadULongAsInt(start_index
236                                            + location * start_offset);
237    if (key < location_start) {
238      // location is below current location
239      top = location;
240    } else {
241      // is key below the upper bound?
242      int32_t location_end = ReadULongAsInt(end_index + location * end_offset);
243#if defined (SFNTLY_DEBUG_FONTDATA)
244      fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
245#endif
246      if (key <= location_end) {
247        return location;
248      } else {
249        // location is above the current location
250        bottom = location + 1;
251      }
252    }
253  }
254  return -1;
255}
256
257CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset,
258                                                int32_t length) {
259  if (offset < 0 || offset + length > Size()) {
260#if !defined (SFNTLY_NO_EXCEPTION)
261    throw IndexOutOfBoundsException(
262        "Attempt to bind data outside of its limits");
263#endif
264    return NULL;
265  }
266  FontDataPtr slice = new ReadableFontData(this, offset, length);
267  return slice.Detach();
268}
269
270CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset) {
271  if (offset < 0 || offset > Size()) {
272#if !defined (SFNTLY_NO_EXCEPTION)
273    throw IndexOutOfBoundsException(
274        "Attempt to bind data outside of its limits");
275#endif
276    return NULL;
277  }
278  FontDataPtr slice = new ReadableFontData(this, offset);
279  return slice.Detach();
280}
281
282ReadableFontData::ReadableFontData(ReadableFontData* data, int32_t offset)
283    : FontData(data, offset),
284      checksum_set_(false),
285      checksum_(0) {
286}
287
288ReadableFontData::ReadableFontData(ReadableFontData* data,
289                                   int32_t offset,
290                                   int32_t length)
291    : FontData(data, offset, length),
292      checksum_set_(false),
293      checksum_(0) {
294}
295
296void ReadableFontData::ComputeChecksum() {
297  // TODO(arthurhsu): IMPLEMENT: synchronization/atomicity
298  int64_t sum = 0;
299  if (checksum_range_.empty()) {
300    sum = ComputeCheckSum(0, Length());
301  } else {
302    for (uint32_t low_bound_index = 0; low_bound_index < checksum_range_.size();
303         low_bound_index += 2) {
304      int32_t low_bound = checksum_range_[low_bound_index];
305      int32_t high_bound = (low_bound_index == checksum_range_.size() - 1) ?
306                                Length() :
307                                checksum_range_[low_bound_index + 1];
308      sum += ComputeCheckSum(low_bound, high_bound);
309    }
310  }
311
312  checksum_ = sum & 0xffffffffL;
313  checksum_set_ = true;
314}
315
316int64_t ReadableFontData::ComputeCheckSum(int32_t low_bound,
317                                          int32_t high_bound) {
318  int64_t sum = 0;
319  // Checksum all whole 4-byte chunks.
320  for (int32_t i = low_bound; i <= high_bound - 4; i += 4) {
321    sum += ReadULong(i);
322  }
323
324  // Add last fragment if not 4-byte multiple
325  int32_t off = high_bound & -4;
326  if (off < high_bound) {
327    int32_t b3 = ReadUByte(off);
328    int32_t b2 = (off + 1 < high_bound) ? ReadUByte(off + 1) : 0;
329    int32_t b1 = (off + 2 < high_bound) ? ReadUByte(off + 2) : 0;
330    int32_t b0 = 0;
331    sum += (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
332  }
333  return sum;
334}
335
336}  // namespace sfntly
337