--- /dev/null
+/*
+ * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
+ *
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+//
+// DbValue.cpp
+//
+
+#include "DbValue.h"
+#include <ctype.h>
+
+//
+// DbValue
+//
+
+DbValue::~DbValue()
+{
+}
+
+//
+// UInt32Value
+//
+
+UInt32Value::UInt32Value(const ReadSection &rs, uint32 &offset)
+: BasicValue<uint32>(rs.at(offset))
+{
+ offset += size();
+}
+
+UInt32Value::UInt32Value(const CSSM_DATA &data)
+{
+ switch (data.Length)
+ {
+ case 1:
+ mValue = *reinterpret_cast<uint8 *>(data.Data);
+ break;
+ case 2:
+ mValue = *reinterpret_cast<uint16 *>(data.Data);
+ break;
+ case 4:
+ mValue = *reinterpret_cast<uint32 *>(data.Data);
+ break;
+ default:
+ CssmError::throwMe(CSSMERR_DL_INVALID_VALUE);
+ }
+}
+
+UInt32Value::~UInt32Value()
+{
+}
+
+void
+UInt32Value::pack(WriteSection &ws, uint32 &offset) const
+{
+ offset = ws.put(offset, mValue);
+}
+
+//
+// SInt32Value
+//
+
+SInt32Value::SInt32Value(const ReadSection &rs, uint32 &offset)
+: BasicValue<sint32>(static_cast<sint32>(rs.at(offset)))
+{
+ offset += size();
+}
+
+SInt32Value::SInt32Value(const CSSM_DATA &data)
+{
+ switch (data.Length)
+ {
+ case 1:
+ mValue = *reinterpret_cast<sint8 *>(data.Data);
+ break;
+ case 2:
+ mValue = *reinterpret_cast<sint16 *>(data.Data);
+ break;
+ case 4:
+ mValue = *reinterpret_cast<sint32 *>(data.Data);
+ break;
+ default:
+ CssmError::throwMe(CSSMERR_DL_INVALID_VALUE);
+ }
+}
+
+SInt32Value::~SInt32Value()
+{
+}
+
+void
+SInt32Value::pack(WriteSection &ws, uint32 &offset) const
+{
+ offset = ws.put(offset, static_cast<uint32>(mValue));
+}
+
+//
+// DoubleValue
+//
+
+DoubleValue::DoubleValue(const ReadSection &rs, uint32 &offset)
+{
+ Range r(offset, size());
+ mValue = *reinterpret_cast<const double *>(rs.range(r));
+ offset += size();
+}
+
+DoubleValue::DoubleValue(const CSSM_DATA &data)
+{
+ switch (data.Length)
+ {
+ case 4:
+ mValue = *reinterpret_cast<float *>(data.Data);
+ break;
+ case 8:
+ mValue = *reinterpret_cast<double *>(data.Data);
+ break;
+ default:
+ CssmError::throwMe(CSSMERR_DL_INVALID_VALUE);
+ }
+}
+
+DoubleValue::~DoubleValue()
+{
+}
+
+void
+DoubleValue::pack(WriteSection &ws, uint32 &offset) const
+{
+ offset = ws.put(offset, size(), bytes());
+}
+
+//
+// BlobValue
+//
+
+BlobValue::BlobValue(const ReadSection &rs, uint32 &offset)
+{
+ Length = rs.at(offset);
+ Data = const_cast<uint8 *>(rs.range(Range(offset + AtomSize, Length)));
+ offset = ReadSection::align(offset + Length + AtomSize);
+}
+
+BlobValue::BlobValue(const CSSM_DATA &data)
+: CssmData(CssmData::overlay(data))
+{
+}
+
+BlobValue::~BlobValue()
+{
+}
+
+void
+BlobValue::pack(WriteSection &ws, uint32 &offset) const
+{
+ offset = ws.put(offset, Length);
+ offset = ws.put(offset, Length, Data);
+}
+
+BlobValue::Comparator::~Comparator()
+{
+}
+
+int
+BlobValue::Comparator::operator () (const uint8 *ptr1, const uint8 *ptr2, uint32 length)
+{
+ return memcmp(ptr1, ptr2, length);
+}
+
+bool
+BlobValue::evaluate(const BlobValue &other, CSSM_DB_OPERATOR op) const
+{
+ return evaluate(*this, other, op, Comparator());
+}
+
+bool
+BlobValue::evaluate(const CssmData &inData1, const CssmData &inData2, CSSM_DB_OPERATOR op,
+ Comparator compare)
+{
+ uint32 length1 = inData1.Length, length2 = inData2.Length;
+ const uint8 *data1 = inData1.Data;
+ const uint8 *data2 = inData2.Data;
+
+ switch (op) {
+
+ case CSSM_DB_CONTAINS_INITIAL_SUBSTRING:
+ if (length1 > length2)
+ return false;
+ length2 = length1;
+ goto DB_EQUAL;
+
+ case CSSM_DB_CONTAINS_FINAL_SUBSTRING:
+ if (length1 > length2)
+ return false;
+ data2 += (length2 - length1);
+ length2 = length1;
+ // dropthrough...
+
+ case CSSM_DB_EQUAL:
+ DB_EQUAL:
+ if (length1 != length2)
+ return false;
+ if (length1 == 0)
+ return true;
+ return compare(data1, data2, length1) == 0;
+
+ case CSSM_DB_NOT_EQUAL:
+ if (length1 != length2)
+ return true;
+ if (length1 == 0)
+ return false;
+ return compare(data1, data2, length1) != 0;
+
+ case CSSM_DB_LESS_THAN:
+ case CSSM_DB_GREATER_THAN:
+ {
+ uint32 length = min(length1, length2);
+ int result = (length == 0) ? 0 : compare(data1, data2, length);
+
+ if (result < 0 || (result == 0 && length1 < length2))
+ return op == CSSM_DB_LESS_THAN;
+ else if (result > 0 || (result == 0 && length1 > length2))
+ return op == CSSM_DB_GREATER_THAN;
+ break;
+ }
+
+ case CSSM_DB_CONTAINS:
+ if (length1 > length2)
+ return false;
+ if (length1 == 0)
+ return true;
+ // Both buffers are at least 1 byte long.
+ for (const uint8 *data = data2; data + length1 <= data2 + length2; data++)
+ if (compare(data1, data, length1) == 0)
+ return true;
+ break;
+
+ default:
+ CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY);
+ }
+
+ return false;
+}
+
+//
+// TimeDateValue
+//
+
+TimeDateValue::TimeDateValue(const ReadSection &rs, uint32 &offset)
+{
+ Length = kTimeDateSize;
+ Data = const_cast<uint8 *>(rs.range(Range(offset, Length)));
+ offset = ReadSection::align(offset + Length);
+}
+
+TimeDateValue::TimeDateValue(const CSSM_DATA &data)
+: BlobValue(data)
+{
+ if (Length != kTimeDateSize || !isValidDate())
+ CssmError::throwMe(CSSMERR_DL_INVALID_VALUE);
+}
+
+TimeDateValue::~TimeDateValue()
+{
+}
+
+void
+TimeDateValue::pack(WriteSection &ws, uint32 &offset) const
+{
+ offset = ws.put(offset, Length, Data);
+}
+
+bool
+TimeDateValue::isValidDate() const
+{
+ if (Length != kTimeDateSize || Data[kTimeDateSize - 1] != 0 ||
+ Data[kTimeDateSize - 2] != 'Z')
+ return false;
+
+ for (uint32 i = 0; i < kTimeDateSize - 2; i++)
+ if (!isdigit(Data[i]))
+ return false;
+
+ uint32 month = rangeValue(4, 2);
+ if (month < 1 || month > 12)
+ return false;
+
+ uint32 day = rangeValue(6, 2);
+ if (day < 1 || day > 31)
+ return false;
+
+ uint32 hour = rangeValue(8, 2);
+ if (hour < 0 || hour > 23)
+ return false;
+
+ uint32 minute = rangeValue(10, 2);
+ if (minute < 0 || minute > 59)
+ return false;
+
+ uint32 second = rangeValue(12, 2);
+ if (second < 0 || second > 59)
+ return false;
+
+ return true;
+}
+
+uint32
+TimeDateValue::rangeValue(uint32 start, uint32 length) const
+{
+ uint32 value = 0;
+ for (uint32 i = 0; i < length; i++)
+ value = value * 10 + Data[start + i] - '0';
+ return value;
+}
+
+//
+// StringValue
+//
+
+StringValue::StringValue(const ReadSection &rs, uint32 &offset)
+: BlobValue(rs, offset)
+{
+}
+
+StringValue::StringValue(const CSSM_DATA &data)
+: BlobValue(data)
+{
+}
+
+StringValue::~StringValue()
+{
+}
+
+int
+StringValue::Comparator::operator () (const uint8 *ptr1, const uint8 *ptr2, uint32 length)
+{
+ return strncmp(reinterpret_cast<const char *>(ptr1),
+ reinterpret_cast<const char *>(ptr2), length);
+}
+
+bool
+StringValue::evaluate(const StringValue &other, CSSM_DB_OPERATOR op) const
+{
+ return BlobValue::evaluate(*this, other, op, StringValue::Comparator());
+}
+
+//
+// BigNumValue
+//
+
+BigNumValue::BigNumValue(const ReadSection &rs, uint32 &offset)
+: BlobValue(rs, offset)
+{
+}
+
+BigNumValue::BigNumValue(const CSSM_DATA &data)
+: BlobValue(data)
+{
+ // remove trailing zero bytes
+ while (Length > 1 && Data[Length - 1] == 0)
+ Length--;
+
+ // if the number is zero (positive or negative), make the length zero
+ if (Length == 1 && (Data[0] & ~kSignBit) == 0)
+ Length = 0;
+}
+
+BigNumValue::~BigNumValue()
+{
+}
+
+// Walk the contents of two equal-sized bignums, moving backward
+// from the high-order bytes, and return the comparison result
+// ala memcmp.
+
+int
+BigNumValue::compare(const uint8 *a, const uint8 *b, int length)
+{
+ for (int diff, i = length - 1; i >= 1; i--)
+ if ((diff = a[i] - b[i]))
+ return diff;
+
+ // for the last (i.e. first) byte, mask out the sign bit
+ return (a[0] & ~kSignBit) - (b[0] & ~kSignBit);
+}
+
+// Compare two bignums, assuming they are in canonical form (i.e.,
+// no bytes containing trailing zeros.
+
+bool
+BigNumValue::evaluate(const BigNumValue &other, CSSM_DB_OPERATOR op) const
+{
+ uint32 length1 = Length, length2 = other.Length;
+ uint8 sign1 = length1 ? (Data[0] & kSignBit) : 0;
+ uint8 sign2 = length2 ? (other.Data[0] & kSignBit) : 0;
+
+ switch (op)
+ {
+ case CSSM_DB_EQUAL:
+ case CSSM_DB_NOT_EQUAL:
+ return BlobValue::evaluate(other, op);
+
+ case CSSM_DB_LESS_THAN:
+ if (sign1 ^ sign2)
+ // different signs: return true iff left value is the negative one
+ return sign1;
+ else if (length1 != length2)
+ // in canonical form, shorter numbers have smaller absolute value
+ return sign1 ? (length1 > length2) : (length1 < length2);
+ else {
+ // same length, same sign...
+ int c = compare(Data, other.Data, length1);
+ return sign1 ? (c > 0) : (c < 0);
+ }
+ break;
+
+ case CSSM_DB_GREATER_THAN:
+ if (sign1 ^ sign2)
+ return sign2;
+ else if (length1 != length2)
+ return sign1 ? (length1 < length2) : (length1 > length2);
+ else {
+ int c = compare(Data, other.Data, length1);
+ return sign1 ? (c < 0) : (c > 0);
+ }
+ break;
+
+ case CSSM_DB_CONTAINS:
+ case CSSM_DB_CONTAINS_INITIAL_SUBSTRING:
+ case CSSM_DB_CONTAINS_FINAL_SUBSTRING:
+ default:
+ CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY);
+ }
+}
+
+//
+// MultiUInt32Value
+//
+
+MultiUInt32Value::MultiUInt32Value(const ReadSection &rs, uint32 &offset)
+{
+ // this is relatively expensive, since it copies the data from the
+ // read section to get the endianness correct
+
+ mNumValues = rs.at(offset);
+ mValues = new uint32[mNumValues];
+
+ for (uint32 i = 0; i < mNumValues; i++)
+ mValues[i] = rs.at(offset + (i + 1) * AtomSize);
+
+ offset = ReadSection::align(offset + (mNumValues + 1) * AtomSize);
+ mOwnsValues = true;
+}
+
+MultiUInt32Value::MultiUInt32Value(const CSSM_DATA &data)
+{
+ if (data.Length & (sizeof(uint32) - 1))
+ CssmError::throwMe(CSSMERR_DL_INVALID_VALUE);
+
+ mNumValues = data.Length / sizeof(uint32);
+ mValues = reinterpret_cast<uint32 *>(data.Data);
+ mOwnsValues = false;
+}
+
+MultiUInt32Value::~MultiUInt32Value()
+{
+ if (mOwnsValues)
+ delete [] mValues;
+}
+
+void
+MultiUInt32Value::pack(WriteSection &ws, uint32 &offset) const
+{
+ offset = ws.put(offset, mNumValues);
+ for (uint32 i = 0; i < mNumValues; i++)
+ offset = ws.put(offset, mValues[i]);
+}
+
+static inline int
+uint32cmp(const uint32 *a, const uint32 *b, uint32 length)
+{
+ return memcmp(a, b, length * sizeof(uint32));
+}
+
+bool
+MultiUInt32Value::evaluate(const MultiUInt32Value &other, CSSM_DB_OPERATOR op) const
+{
+ uint32 length1 = mNumValues, length2 = other.mNumValues;
+ const uint32 *values1 = mValues;
+ const uint32 *values2 = other.mValues;
+
+ switch (op)
+ {
+ case CSSM_DB_EQUAL:
+ if (length1 == length2)
+ return uint32cmp(values1, values2, length1) == 0;
+ break;
+
+ case CSSM_DB_NOT_EQUAL:
+ if (length1 != length2 || uint32cmp(values1, values2, length1))
+ return true;
+ break;
+
+ case CSSM_DB_CONTAINS_INITIAL_SUBSTRING:
+ if (length1 <= length2)
+ return uint32cmp(values1, values2, length1) == 0;
+ break;
+
+ case CSSM_DB_CONTAINS_FINAL_SUBSTRING:
+ if (length1 <= length2)
+ return uint32cmp(values1, values2 + (length2 - length1), length1) == 0;
+ break;
+
+ case CSSM_DB_CONTAINS:
+ if (length1 <= length2) {
+
+ if (length1 == 0)
+ return true;
+
+ for (const uint32 *values = values2; values + length1 < values2 + length2; values++)
+ if (uint32cmp(values1, values, length1) == 0)
+ return true;
+ }
+ break;
+
+ case CSSM_DB_LESS_THAN:
+ // this is not required by the spec, but is required to sort indexes over
+ // multi uint32 keys...
+ if (length1 < length2)
+ return true;
+ else if (length1 == length2)
+ return uint32cmp(values1, values2, length1) < 0;
+ break;
+
+ default:
+ CssmError::throwMe(CSSMERR_DL_UNSUPPORTED_QUERY);
+ }
+
+ return false;
+}
+
+