/* * 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 // // DbValue // DbValue::~DbValue() { } // // UInt32Value // UInt32Value::UInt32Value(const ReadSection &rs, uint32 &offset) : BasicValue(rs.at(offset)) { offset += size(); } UInt32Value::UInt32Value(const CSSM_DATA &data) { switch (data.Length) { case 1: mValue = *reinterpret_cast(data.Data); break; case 2: mValue = *reinterpret_cast(data.Data); break; case 4: mValue = *reinterpret_cast(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(static_cast(rs.at(offset))) { offset += size(); } SInt32Value::SInt32Value(const CSSM_DATA &data) { switch (data.Length) { case 1: mValue = *reinterpret_cast(data.Data); break; case 2: mValue = *reinterpret_cast(data.Data); break; case 4: mValue = *reinterpret_cast(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(mValue)); } // // DoubleValue // DoubleValue::DoubleValue(const ReadSection &rs, uint32 &offset) { Range r(offset, size()); mValue = *reinterpret_cast(rs.range(r)); offset += size(); } DoubleValue::DoubleValue(const CSSM_DATA &data) { switch (data.Length) { case 4: mValue = *reinterpret_cast(data.Data); break; case 8: mValue = *reinterpret_cast(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(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(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(ptr1), reinterpret_cast(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(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; }