blob: 06d783f2e331a560f34c312e22d1bb45329e2d78 [file] [log] [blame]
/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sfntly/data/readable_font_data.h"
#include <stdio.h>
#include "sfntly/data/memory_byte_array.h"
#include "sfntly/data/writable_font_data.h"
#include "sfntly/port/exception_type.h"
namespace sfntly {
ReadableFontData::ReadableFontData(ByteArray* array)
: FontData(array),
checksum_set_(false),
checksum_(0) {
}
ReadableFontData::~ReadableFontData() {}
// TODO(arthurhsu): re-investigate the memory model of this function. It's
// not too useful without copying, but it's not performance
// savvy to do copying.
CALLER_ATTACH
ReadableFontData* ReadableFontData::CreateReadableFontData(ByteVector* b) {
assert(b);
ByteArrayPtr ba = new MemoryByteArray(b->size());
ba->Put(0, b);
ReadableFontDataPtr wfd = new ReadableFontData(ba);
return wfd.Detach();
}
int64_t ReadableFontData::Checksum() {
AutoLock lock(checksum_lock_);
if (!checksum_set_) {
ComputeChecksum();
}
return checksum_;
}
void ReadableFontData::SetCheckSumRanges(const IntegerList& ranges) {
checksum_range_ = ranges;
checksum_set_ = false; // UNIMPLEMENTED: atomicity
}
int32_t ReadableFontData::ReadUByte(int32_t index) {
int32_t b = array_->Get(BoundOffset(index));
#if !defined (SFNTLY_NO_EXCEPTION)
if (b < 0) {
throw IndexOutOfBoundException(
"Index attempted to be read from is out of bounds", index);
}
#endif
return b;
}
int32_t ReadableFontData::ReadByte(int32_t index) {
int32_t b = array_->Get(BoundOffset(index));
#if !defined (SFNTLY_NO_EXCEPTION)
if (b < 0) {
throw IndexOutOfBoundException(
"Index attempted to be read from is out of bounds", index);
}
#endif
return (b << 24) >> 24;
}
int32_t ReadableFontData::ReadBytes(int32_t index,
byte_t* b,
int32_t offset,
int32_t length) {
return array_->Get(BoundOffset(index), b, offset, BoundLength(index, length));
}
int32_t ReadableFontData::ReadChar(int32_t index) {
return ReadUByte(index);
}
int32_t ReadableFontData::ReadUShort(int32_t index) {
return 0xffff & (ReadUByte(index) << 8 | ReadUByte(index + 1));
}
int32_t ReadableFontData::ReadShort(int32_t index) {
return ((ReadByte(index) << 8 | ReadUByte(index + 1)) << 16) >> 16;
}
int32_t ReadableFontData::ReadUInt24(int32_t index) {
return 0xffffff & (ReadUByte(index) << 16 |
ReadUByte(index + 1) << 8 |
ReadUByte(index + 2));
}
int64_t ReadableFontData::ReadULong(int32_t index) {
return 0xffffffffL & (ReadUByte(index) << 24 |
ReadUByte(index + 1) << 16 |
ReadUByte(index + 2) << 8 |
ReadUByte(index + 3));
}
int32_t ReadableFontData::ReadULongAsInt(int32_t index) {
int64_t ulong = ReadULong(index);
#if !defined (SFNTLY_NO_EXCEPTION)
if ((ulong & 0x80000000) == 0x80000000) {
throw ArithmeticException("Long value too large to fit into an integer.");
}
#endif
return static_cast<int32_t>(ulong);
}
int64_t ReadableFontData::ReadULongLE(int32_t index) {
return 0xffffffffL & (ReadUByte(index) |
ReadUByte(index + 1) << 8 |
ReadUByte(index + 2) << 16 |
ReadUByte(index + 3) << 24);
}
int32_t ReadableFontData::ReadLong(int32_t index) {
return ReadByte(index) << 24 |
ReadUByte(index + 1) << 16 |
ReadUByte(index + 2) << 8 |
ReadUByte(index + 3);
}
int32_t ReadableFontData::ReadFixed(int32_t index) {
return ReadLong(index);
}
int64_t ReadableFontData::ReadDateTimeAsLong(int32_t index) {
return (int64_t)ReadULong(index) << 32 | ReadULong(index + 4);
}
int32_t ReadableFontData::ReadFWord(int32_t index) {
return ReadShort(index);
}
int32_t ReadableFontData::ReadFUFWord(int32_t index) {
return ReadUShort(index);
}
int32_t ReadableFontData::CopyTo(OutputStream* os) {
return array_->CopyTo(os, BoundOffset(0), Length());
}
int32_t ReadableFontData::CopyTo(WritableFontData* wfd) {
return array_->CopyTo(wfd->BoundOffset(0),
wfd->array_,
BoundOffset(0),
Length());
}
int32_t ReadableFontData::CopyTo(ByteArray* ba) {
return array_->CopyTo(ba, BoundOffset(0), Length());
}
int32_t ReadableFontData::SearchUShort(int32_t start_index,
int32_t start_offset,
int32_t end_index,
int32_t end_offset,
int32_t length,
int32_t key) {
int32_t location = 0;
int32_t bottom = 0;
int32_t top = length;
while (top != bottom) {
location = (top + bottom) / 2;
int32_t location_start = ReadUShort(start_index + location * start_offset);
if (key < location_start) {
// location is below current location
top = location;
} else {
// is key below the upper bound?
int32_t location_end = ReadUShort(end_index + location * end_offset);
#if defined (SFNTLY_DEBUG_FONTDATA)
fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
#endif
if (key <= location_end) {
return location;
} else {
// location is above the current location
bottom = location + 1;
}
}
}
return -1;
}
int32_t ReadableFontData::SearchUShort(int32_t start_index,
int32_t start_offset,
int32_t length,
int32_t key) {
int32_t location = 0;
int32_t bottom = 0;
int32_t top = length;
while (top != bottom) {
location = (top + bottom) / 2;
int32_t location_start = ReadUShort(start_index + location * start_offset);
if (key < location_start) {
// location is below current location
top = location;
} else if (key > location_start) {
// location is above current location
bottom = location + 1;
} else {
return location;
}
}
return -1;
}
int32_t ReadableFontData::SearchULong(int32_t start_index,
int32_t start_offset,
int32_t end_index,
int32_t end_offset,
int32_t length,
int32_t key) {
int32_t location = 0;
int32_t bottom = 0;
int32_t top = length;
while (top != bottom) {
location = (top + bottom) / 2;
int32_t location_start = ReadULongAsInt(start_index
+ location * start_offset);
if (key < location_start) {
// location is below current location
top = location;
} else {
// is key below the upper bound?
int32_t location_end = ReadULongAsInt(end_index + location * end_offset);
#if defined (SFNTLY_DEBUG_FONTDATA)
fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
#endif
if (key <= location_end) {
return location;
} else {
// location is above the current location
bottom = location + 1;
}
}
}
return -1;
}
CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset,
int32_t length) {
if (offset < 0 || offset + length > Size()) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundsException(
"Attempt to bind data outside of its limits");
#endif
return NULL;
}
FontDataPtr slice = new ReadableFontData(this, offset, length);
return slice.Detach();
}
CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset) {
if (offset < 0 || offset > Size()) {
#if !defined (SFNTLY_NO_EXCEPTION)
throw IndexOutOfBoundsException(
"Attempt to bind data outside of its limits");
#endif
return NULL;
}
FontDataPtr slice = new ReadableFontData(this, offset);
return slice.Detach();
}
ReadableFontData::ReadableFontData(ReadableFontData* data, int32_t offset)
: FontData(data, offset),
checksum_set_(false),
checksum_(0) {
}
ReadableFontData::ReadableFontData(ReadableFontData* data,
int32_t offset,
int32_t length)
: FontData(data, offset, length),
checksum_set_(false),
checksum_(0) {
}
void ReadableFontData::ComputeChecksum() {
// TODO(arthurhsu): IMPLEMENT: synchronization/atomicity
int64_t sum = 0;
if (checksum_range_.empty()) {
sum = ComputeCheckSum(0, Length());
} else {
for (uint32_t low_bound_index = 0; low_bound_index < checksum_range_.size();
low_bound_index += 2) {
int32_t low_bound = checksum_range_[low_bound_index];
int32_t high_bound = (low_bound_index == checksum_range_.size() - 1) ?
Length() :
checksum_range_[low_bound_index + 1];
sum += ComputeCheckSum(low_bound, high_bound);
}
}
checksum_ = sum & 0xffffffffL;
checksum_set_ = true;
}
int64_t ReadableFontData::ComputeCheckSum(int32_t low_bound,
int32_t high_bound) {
int64_t sum = 0;
// Checksum all whole 4-byte chunks.
for (int32_t i = low_bound; i <= high_bound - 4; i += 4) {
sum += ReadULong(i);
}
// Add last fragment if not 4-byte multiple
int32_t off = high_bound & -4;
if (off < high_bound) {
int32_t b3 = ReadUByte(off);
int32_t b2 = (off + 1 < high_bound) ? ReadUByte(off + 1) : 0;
int32_t b1 = (off + 2 < high_bound) ? ReadUByte(off + 2) : 0;
int32_t b0 = 0;
sum += (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
}
return sum;
}
} // namespace sfntly