| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| /*------------------------------------------------------------------------- |
| * |
| * cdbvarblock.c |
| * A general data package that has variable-length item array that can be |
| * efficiently indexed once complete. |
| * |
| * (See .h file for usage comments) |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "cdb/cdbvarblock.h" |
| |
| static VarBlockByteLen VarBlockGetItemLen( |
| VarBlockReader *varBlockReader, |
| int itemIndex); |
| static void VarBlockGetItemPtrAndLen( |
| VarBlockReader *varBlockReader, |
| int itemIndex, |
| uint8 **itemPtr, |
| VarBlockByteLen *itemLen); |
| static VarBlockByteOffset VarBlockGetOffset( |
| VarBlockHeader *header, |
| VarBlockByteOffset offsetToOffsetArray, |
| int itemIndex); |
| |
| |
| /* |
| * Initialize the VarBlock maker. |
| * |
| * Since we are going to pack the item offset array |
| * right after the variable-length item array when |
| * finished, we need a temporary buffer for the |
| * offsets while we are making the block. The caller |
| * supplies the tempScratchSpace parameter for this |
| * purpose. |
| * |
| * Note that tempScratchSpaceLen in effect |
| * serves as the maximum number of items (UNDONE: more on 2-byte vs. 3-byte). |
| */ |
| void VarBlockMakerInit( |
| VarBlockMaker *varBlockMaker, |
| uint8 *buffer, |
| VarBlockByteLen maxBufferLen, |
| uint8 *tempScratchSpace, |
| int tempScratchSpaceLen) |
| { |
| Assert(varBlockMaker != NULL); |
| Assert(buffer != NULL); // UNDONE: And ALIGN(4) |
| Assert(maxBufferLen >= VARBLOCK_HEADER_LEN); // UNDONE: And even. |
| Assert(tempScratchSpace != NULL); // UNDONE: And ALIGN(4). |
| Assert(tempScratchSpaceLen > 0); // UNDONE: And even. |
| |
| varBlockMaker->header = (VarBlockHeader*)buffer; |
| varBlockMaker->maxBufferLen = maxBufferLen; |
| varBlockMaker->currentItemLenSum = 0; |
| |
| varBlockMaker->tempScratchSpace = tempScratchSpace; |
| varBlockMaker->tempScratchSpaceLen = tempScratchSpaceLen; |
| |
| varBlockMaker->nextItemPtr = &buffer[VARBLOCK_HEADER_LEN]; |
| |
| // UNDONE: Fix the constant to be the $MIN of approx. 16k (or 32k) |
| // UNDONE: and maxBufferLen... |
| varBlockMaker->last2ByteOffsetPtr = &buffer[63 * 1024]; |
| varBlockMaker->currentItemCount = 0; |
| varBlockMaker->maxItemCount = tempScratchSpaceLen / 2; |
| |
| memset(buffer, 0, VARBLOCK_HEADER_LEN); |
| VarBlockSet_version(varBlockMaker->header,InitialVersion); |
| VarBlockSet_offsetsAreSmall(varBlockMaker->header,true); |
| } |
| |
| /* |
| * Get a pointer to the next variable-length item so it can |
| * be filled in. |
| * |
| * Returns NULL when there is no more space left in the VarBlock. |
| */ |
| uint8* VarBlockMakerGetNextItemPtr( |
| VarBlockMaker *varBlockMaker, |
| VarBlockByteLen itemLen) |
| { |
| int currentItemCount; |
| uint8 *buffer; |
| uint8 *nextItemPtr; |
| VarBlockByteOffset nextItemOffset; |
| VarBlockByteLen newItemLenSum; |
| VarBlockByteLen offsetArrayLenRounded; |
| VarBlockByteLen newTotalLen; |
| uint8 *tempScratchSpace; |
| VarBlockByteOffset16 *tempOffsetArray16; |
| VarBlockByteOffset24 tempOffset24; |
| |
| Assert(varBlockMaker != NULL); |
| Assert(itemLen >= 0); |
| |
| currentItemCount = varBlockMaker->currentItemCount; |
| if (currentItemCount >= varBlockMaker->maxItemCount) |
| { |
| /* |
| * Reached a limit on the number of items based on the |
| * scratch space... |
| */ |
| return NULL; |
| } |
| |
| buffer = (uint8*)varBlockMaker->header; |
| nextItemPtr = varBlockMaker->nextItemPtr; |
| nextItemOffset = (VarBlockByteOffset)(nextItemPtr - buffer); |
| newItemLenSum = varBlockMaker->currentItemLenSum + itemLen; |
| tempScratchSpace = varBlockMaker->tempScratchSpace; |
| |
| /* |
| * We require VarBlocks to given to us on 4 byte boundaries... |
| */ |
| if (VarBlockGet_offsetsAreSmall(varBlockMaker->header)) |
| { |
| int newMaxItemCount; |
| |
| /* |
| * Will small 2 byte offsets still work? |
| */ |
| if (nextItemPtr > varBlockMaker->last2ByteOffsetPtr) |
| { |
| int r; |
| |
| /* |
| * Try out large 3-byte offsets. |
| */ |
| |
| /* |
| * Round-up to even length. |
| */ |
| offsetArrayLenRounded = (currentItemCount + 1) * |
| VARBLOCK_BYTE_OFFSET_24_LEN; |
| offsetArrayLenRounded = ((offsetArrayLenRounded + 1)/2)*2; |
| |
| newTotalLen = VARBLOCK_HEADER_LEN + |
| ((newItemLenSum + 1)/2)*2 + |
| offsetArrayLenRounded; |
| |
| if (newTotalLen > varBlockMaker->maxBufferLen) |
| return NULL; // Out of space. |
| |
| newMaxItemCount = varBlockMaker->tempScratchSpaceLen / |
| VARBLOCK_BYTE_OFFSET_24_LEN; |
| if (currentItemCount >= newMaxItemCount) |
| { |
| /* |
| * Reached a limit on the number of items based on the |
| * scratch space with large offsets... |
| */ |
| return NULL; |
| } |
| |
| VarBlockSet_offsetsAreSmall(varBlockMaker->header,false); |
| varBlockMaker->maxItemCount = newMaxItemCount; |
| |
| /* |
| * Reverse move to use 3-byte offsets. |
| */ |
| tempOffsetArray16 = (VarBlockByteOffset16*)tempScratchSpace; |
| for (r = currentItemCount; r >= 0; r--) |
| { |
| tempOffset24.byteOffset24 = tempOffsetArray16[r]; |
| |
| memcpy(tempScratchSpace + (r * VARBLOCK_BYTE_OFFSET_24_LEN), |
| &tempOffset24, |
| VARBLOCK_BYTE_OFFSET_24_LEN); |
| } |
| |
| tempOffset24.byteOffset24 = nextItemOffset; |
| memcpy(tempScratchSpace + |
| (currentItemCount * VARBLOCK_BYTE_OFFSET_24_LEN), |
| &tempOffset24, |
| VARBLOCK_BYTE_OFFSET_24_LEN); |
| } |
| else |
| { |
| /* |
| * Normal calculation. |
| */ |
| newTotalLen = VARBLOCK_HEADER_LEN + |
| ((newItemLenSum + 1)/2)* 2 + |
| currentItemCount * 2 + 2; |
| if (newTotalLen > varBlockMaker->maxBufferLen) |
| return NULL; // Out of space. |
| |
| tempOffsetArray16 = (VarBlockByteOffset16*)tempScratchSpace; |
| tempOffsetArray16[currentItemCount] = |
| (VarBlockByteOffset16)nextItemOffset; |
| } |
| } |
| else |
| { |
| /* |
| * Large 3 byte ofsets. |
| */ |
| |
| /* |
| * Round-up to even length. |
| */ |
| offsetArrayLenRounded = (currentItemCount + 1) * |
| VARBLOCK_BYTE_OFFSET_24_LEN; |
| offsetArrayLenRounded = ((offsetArrayLenRounded + 1)/2)*2; |
| |
| newTotalLen = VARBLOCK_HEADER_LEN + |
| ((newItemLenSum + 1)/2)*2 + |
| offsetArrayLenRounded; |
| |
| if (newTotalLen > varBlockMaker->maxBufferLen) |
| return NULL; // Out of space. |
| |
| tempOffset24.byteOffset24 = nextItemOffset; |
| memcpy(tempScratchSpace + |
| (currentItemCount * VARBLOCK_BYTE_OFFSET_24_LEN), |
| &tempOffset24, |
| VARBLOCK_BYTE_OFFSET_24_LEN); |
| } |
| |
| varBlockMaker->nextItemPtr += itemLen; |
| varBlockMaker->currentItemCount++; |
| varBlockMaker->currentItemLenSum = newItemLenSum; |
| |
| return nextItemPtr; |
| } |
| |
| /* |
| * Get the variable-length item count. |
| */ |
| int VarBlockMakerItemCount( |
| VarBlockMaker *varBlockMaker) |
| { |
| Assert(varBlockMaker != NULL); |
| |
| return varBlockMaker->currentItemCount; |
| } |
| |
| /* |
| * Finish making the VarBlock. |
| * |
| * The item-offsets array will be added to the end. |
| */ |
| VarBlockByteLen VarBlockMakerFinish( |
| VarBlockMaker *varBlockMaker) |
| { |
| uint8 *buffer; |
| int itemCount; |
| VarBlockByteLen itemLenSum; |
| uint8 *tempScratchSpace; |
| VarBlockByteOffset offsetToOffsetArray; |
| int z; |
| int multiplier; |
| VarBlockByteLen offsetArrayLen; |
| VarBlockByteLen offsetArrayLenRounded; |
| VarBlockByteLen bufferLen; |
| |
| Assert(varBlockMaker != NULL); |
| |
| buffer = (uint8*)varBlockMaker->header; |
| itemCount = varBlockMaker->currentItemCount; |
| itemLenSum = varBlockMaker->currentItemLenSum; |
| |
| tempScratchSpace = varBlockMaker->tempScratchSpace; |
| |
| /* |
| * offsetArrays start on even boundary. |
| */ |
| offsetToOffsetArray = VARBLOCK_HEADER_LEN + |
| ((itemLenSum + 1)/2)* 2; |
| |
| if (VarBlockGet_offsetsAreSmall(varBlockMaker->header)) |
| { |
| memcpy(&buffer[offsetToOffsetArray], tempScratchSpace, itemCount * 2); |
| |
| multiplier = 2; |
| } |
| else |
| { |
| memcpy(&buffer[offsetToOffsetArray], |
| tempScratchSpace, |
| itemCount * VARBLOCK_BYTE_OFFSET_24_LEN); |
| |
| // UNDONE: Zero out odd byte. |
| |
| multiplier = VARBLOCK_BYTE_OFFSET_24_LEN; |
| } |
| |
| /* |
| * Zero the pad between the last item and the offset array for data |
| * security. |
| */ |
| for (z = VARBLOCK_HEADER_LEN + itemLenSum; z < offsetToOffsetArray; z++) |
| { |
| buffer[z] = 0; |
| } |
| |
| VarBlockSet_itemLenSum(varBlockMaker->header,itemLenSum); |
| VarBlockSet_itemCount(varBlockMaker->header,itemCount); |
| |
| /* |
| * Round-up to even length. |
| */ |
| offsetArrayLen = itemCount * multiplier; |
| offsetArrayLenRounded = ((offsetArrayLen + 1)/2)*2; |
| |
| /* |
| * Zero the pad between the last offset and the rounded-up end |
| * of VarBlock for data security. |
| */ |
| for (z = offsetToOffsetArray + offsetArrayLen; |
| z < offsetToOffsetArray + offsetArrayLenRounded; |
| z++) |
| { |
| buffer[z] = 0; |
| } |
| |
| bufferLen = offsetToOffsetArray + offsetArrayLenRounded; |
| |
| //#ifdef DEBUG |
| // if(VarBlockIsValid(buffer, bufferLen) != VarBlockCheckOk) |
| // { |
| // // UNDONE: Use elog. |
| // fprintf(stderr, "VarBlockIsValid not ok (detail = '%s')", |
| // VarBlockCheckErrorStr); |
| // exit(1); |
| // } |
| //#endif |
| |
| return bufferLen; |
| } |
| |
| /* |
| * Reset the VarBlock maker so it can make another one |
| * using the same inputs as given to VarBlockMakerInit. |
| */ |
| void VarBlockMakerReset( |
| VarBlockMaker *varBlockMaker) |
| { |
| uint8 *buffer; |
| |
| Assert(varBlockMaker != NULL); |
| |
| buffer = (uint8*)varBlockMaker->header; |
| |
| memset(buffer, 0, VARBLOCK_HEADER_LEN); |
| VarBlockSet_version(varBlockMaker->header,InitialVersion); |
| VarBlockSet_offsetsAreSmall(varBlockMaker->header,true); |
| |
| varBlockMaker->currentItemLenSum = 0; |
| |
| varBlockMaker->nextItemPtr = &buffer[VARBLOCK_HEADER_LEN]; |
| varBlockMaker->currentItemCount = 0; |
| varBlockMaker->maxItemCount = varBlockMaker->tempScratchSpaceLen / 2; |
| |
| } |
| |
| static VarBlockByteOffset VarBlockGetOffset( |
| VarBlockHeader *header, |
| VarBlockByteOffset offsetToOffsetArray, |
| int itemIndex) |
| { |
| uint8 *offsetArray; |
| |
| Assert(header != NULL); |
| Assert(offsetToOffsetArray >= VARBLOCK_HEADER_LEN); |
| Assert(itemIndex >= 0); |
| Assert(itemIndex < VarBlockGet_itemCount(header)); |
| |
| offsetArray = ((uint8*)header) + offsetToOffsetArray; |
| |
| if (VarBlockGet_offsetsAreSmall(header)) |
| { |
| return ((VarBlockByteOffset16*)offsetArray)[itemIndex]; |
| } |
| else |
| { |
| VarBlockByteOffset24 offset24; |
| |
| memcpy(&offset24, |
| offsetArray + (itemIndex * VARBLOCK_BYTE_OFFSET_24_LEN), |
| VARBLOCK_BYTE_OFFSET_24_LEN); |
| |
| return offset24.byteOffset24; |
| } |
| } |
| |
| #define MAX_VARBLOCK_CHECK_ERROR_STR 200 |
| static char VarBlockCheckErrorStr[MAX_VARBLOCK_CHECK_ERROR_STR] = "\0"; |
| |
| /* |
| * Return a string message for the last check error. |
| */ |
| char *VarBlockGetCheckErrorStr(void) |
| { |
| return VarBlockCheckErrorStr; |
| } |
| |
| /* |
| * Determine if the header looks valid. |
| * |
| * peekLen must be at least VARBLOCK_HEADER_LEN bytes. |
| */ |
| VarBlockCheckError VarBlockHeaderIsValid( |
| uint8 *buffer, |
| VarBlockByteLen peekLen) |
| { |
| VarBlockHeader *header; |
| |
| Assert(buffer != NULL); |
| Assert(peekLen >= VARBLOCK_HEADER_LEN); |
| |
| header = (VarBlockHeader*)buffer; |
| |
| if (VarBlockGet_version(header) != InitialVersion) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "Invalid initial version %d (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| VarBlockGet_version(header), |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckBadVersion; |
| } |
| |
| if (VarBlockGet_reserved(header) != 0) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "Reserved not 0 (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckReservedNot0; |
| } |
| |
| if (VarBlockGet_moreReserved(header) != 0) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "More reserved not 0 (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckMoreReservedNot0; |
| } |
| |
| /* |
| * Can't do checking of itemSumLen and itemCount without access to whole |
| * VarBlock. |
| */ |
| |
| return VarBlockCheckOk; |
| } |
| |
| /* |
| * Determine if the whole VarBlock looks valid. |
| * |
| * bufferLen must be VarBlock length. |
| */ |
| VarBlockCheckError VarBlockIsValid( |
| uint8 *buffer, |
| VarBlockByteLen bufferLen) |
| { |
| VarBlockHeader *header; |
| int headerLen = VARBLOCK_HEADER_LEN; |
| VarBlockCheckError varBlockCheckError; |
| int itemLenSum; |
| int itemCount; |
| VarBlockByteOffset offsetToOffsetArray; |
| int z; |
| |
| Assert(buffer != NULL); |
| Assert(bufferLen >= VARBLOCK_HEADER_LEN); |
| |
| header = (VarBlockHeader*)buffer; |
| |
| varBlockCheckError = VarBlockHeaderIsValid(buffer, bufferLen); |
| if (varBlockCheckError != VarBlockCheckOk) |
| return varBlockCheckError; |
| |
| /* |
| * Now do checking of itemSumLen and itemCount. |
| */ |
| itemLenSum = VarBlockGet_itemLenSum(header); |
| if (itemLenSum >= bufferLen) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "itemLenSum %d greater than or equal to bufferLen %d (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| itemLenSum, |
| bufferLen, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckItemSumLenBad1; // #1 |
| } |
| |
| offsetToOffsetArray = VARBLOCK_HEADER_LEN + |
| ((itemLenSum + 1)/2)* 2; |
| if (offsetToOffsetArray > bufferLen) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "offsetToOffsetArray %d greater than bufferLen %d (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| offsetToOffsetArray, |
| bufferLen, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckItemSumLenBad2; // #2 |
| } |
| |
| /* |
| * Verify the data security zero pad between the last item and |
| * the the offset array. |
| */ |
| for (z = VARBLOCK_HEADER_LEN + itemLenSum; z < offsetToOffsetArray; z++) |
| { |
| if (buffer[z] != 0) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "Bad zero pad at offset %d between items and offset array (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| z, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckZeroPadBad1; // #1 |
| } |
| } |
| |
| itemCount = VarBlockGet_itemCount(header); |
| if (itemCount == 0) |
| { |
| if (offsetToOffsetArray != bufferLen) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "offsetToOffsetArray %d should equal bufferLen %d for itemCount 0 (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| offsetToOffsetArray, |
| bufferLen, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckItemCountBad1; // #1 |
| } |
| } |
| else |
| { |
| int multiplier; |
| VarBlockByteLen actualOffsetArrayLenRounded; |
| VarBlockByteLen calculatedOffsetArrayLen; |
| VarBlockByteLen calculatedOffsetArrayLenRounded; |
| VarBlockByteOffset offset; |
| int i; |
| |
| if (VarBlockGet_offsetsAreSmall(header)) |
| { |
| multiplier = 2; |
| } |
| else |
| { |
| multiplier = VARBLOCK_BYTE_OFFSET_24_LEN; |
| } |
| |
| actualOffsetArrayLenRounded = bufferLen - offsetToOffsetArray; |
| |
| // Since itemCount was from 24-bits and the multipler is small, |
| // this multiply should be safe. |
| calculatedOffsetArrayLen = itemCount * multiplier; |
| calculatedOffsetArrayLenRounded = |
| ((calculatedOffsetArrayLen + 1)/2)*2; |
| if (actualOffsetArrayLenRounded != calculatedOffsetArrayLenRounded) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "actual OffsetArray length %d should equal calculated OffsetArray length %d for itemCount %d (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| actualOffsetArrayLenRounded, |
| calculatedOffsetArrayLenRounded, |
| itemCount, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckItemCountBad2; // #2 |
| } |
| |
| /* |
| * The first offset is right after the header. |
| */ |
| offset = VarBlockGetOffset(header, offsetToOffsetArray, 0); |
| if (offset != VARBLOCK_HEADER_LEN) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "offset %d at index 0 is bad -- must equal %d (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| offset, |
| headerLen, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckOffsetBad1; // #1 |
| } |
| |
| for (i = 1; i < itemCount; i++) |
| { |
| VarBlockByteOffset prevOffset = offset; |
| |
| if (offset < prevOffset) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "offset %d at index %d is bad -- less than previous offset %d (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| offset, |
| i, |
| prevOffset, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckOffsetBad2; // #2 |
| } |
| if (offset > itemLenSum + VARBLOCK_HEADER_LEN) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "offset %d at index %d is bad -- greater than itemLenSum and header %d (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| offset, |
| i, |
| itemLenSum + headerLen, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckOffsetBad3; // #3 |
| } |
| } |
| |
| /* |
| * Verify the data security zero pad between the last offset and |
| * the rounded-up end of the VarBlock. |
| */ |
| for (z = offsetToOffsetArray + calculatedOffsetArrayLen; |
| z < offsetToOffsetArray + calculatedOffsetArrayLenRounded; |
| z++) |
| { |
| if (buffer[z] != 0) |
| { |
| sprintf(VarBlockCheckErrorStr, |
| "Bad zero pad at offset %d between last offset and the rounded-up end of buffer (bytes_0_3 0x%08x, bytes_4_7 0x%08x)", |
| z, |
| header->bytes_0_3, header->bytes_4_7); |
| return VarBlockCheckZeroPadBad2; // #2 |
| } |
| } |
| |
| } |
| |
| return VarBlockCheckOk; |
| } |
| |
| /* |
| * Given a pointer to a VarBlock with at least VARBLOCK_HEADER_LEN bytes |
| * present, return the length of the whole block. |
| */ |
| VarBlockByteLen VarBlockLenFromHeader( |
| uint8 *buffer, |
| VarBlockByteLen peekLen) |
| { |
| VarBlockHeader *header; |
| VarBlockByteLen itemLenSum; |
| VarBlockByteOffset offsetToOffsetArray; |
| int multiplier; |
| VarBlockByteLen bufferLen; |
| int headerLen = VARBLOCK_HEADER_LEN; |
| VarBlockByteLen offsetArrayLen; |
| |
| Assert(buffer != NULL); |
| if(peekLen < headerLen) |
| { |
| fprintf(stderr,"bufferLen %d minimum %d", |
| peekLen, headerLen); |
| exit(1); |
| } |
| Assert(peekLen >= VARBLOCK_HEADER_LEN); |
| |
| //#ifdef DEBUG |
| // if(!VarBlockHeaderIsValid(buffer, peekLen)) |
| // { |
| // // UNDONE: Use elog. |
| // fprintf(stderr, "VarBlockHeaderIsValid not ok (detail = '%s')", |
| // VarBlockCheckErrorStr); |
| // exit(1); |
| // } |
| //#endif |
| |
| header = (VarBlockHeader*)buffer; |
| |
| itemLenSum = VarBlockGet_itemLenSum(header); |
| |
| /* |
| * Start offsetArrays on even boundary. |
| */ |
| offsetToOffsetArray = VARBLOCK_HEADER_LEN + |
| ((itemLenSum + 1)/2)* 2; |
| |
| if (VarBlockGet_offsetsAreSmall(header)) |
| { |
| multiplier = 2; |
| } |
| else |
| { |
| multiplier = VARBLOCK_BYTE_OFFSET_24_LEN; |
| } |
| |
| /* |
| * Round-up to even length. |
| */ |
| offsetArrayLen = VarBlockGet_itemCount(header) * multiplier; |
| offsetArrayLen = ((offsetArrayLen + 1)/2)*2; |
| |
| bufferLen = offsetToOffsetArray + offsetArrayLen; |
| |
| return bufferLen; |
| } |
| |
| /* |
| * Initialize the VarBlock reader. |
| */ |
| void VarBlockReaderInit( |
| VarBlockReader *varBlockReader, |
| uint8 *buffer, |
| VarBlockByteLen bufferLen) |
| { |
| VarBlockHeader *header; |
| VarBlockByteLen itemLenSum; |
| VarBlockByteOffset offsetToOffsetArray; |
| int divisor; |
| int calculatedItemCount; |
| |
| Assert(varBlockReader != NULL); |
| Assert(buffer != NULL); // UNDONE: And ALIGN(4) |
| Assert(bufferLen >= VARBLOCK_HEADER_LEN); // UNDONE: And even. |
| //#ifdef DEBUG |
| // if(VarBlockIsValid(buffer, bufferLen) != VarBlockCheckOk) |
| // { |
| // // UNDONE: Use elog. |
| // fprintf(stderr, "VarBlockIsValid not ok (detail = '%s')", |
| // VarBlockCheckErrorStr); |
| // exit(1); |
| // } |
| //#endif |
| |
| header = (VarBlockHeader*)buffer; |
| |
| varBlockReader->header = header; |
| varBlockReader->bufferLen = bufferLen; |
| |
| itemLenSum = VarBlockGet_itemLenSum(header); |
| |
| /* |
| * offsetArrays start on even boundary. |
| */ |
| offsetToOffsetArray = VARBLOCK_HEADER_LEN + |
| ((itemLenSum + 1)/2)* 2; |
| if (VarBlockGet_offsetsAreSmall(header)) |
| { |
| divisor = 2; |
| } |
| else |
| { |
| divisor = VARBLOCK_BYTE_OFFSET_24_LEN; |
| } |
| calculatedItemCount = (bufferLen - offsetToOffsetArray) / divisor; |
| Assert(calculatedItemCount == VarBlockGet_itemCount(header)); |
| |
| varBlockReader->offsetToOffsetArray = offsetToOffsetArray; |
| varBlockReader->nextIndex = 0; |
| varBlockReader->nextItemPtr = &buffer[VARBLOCK_HEADER_LEN]; |
| } |
| |
| /* |
| * Set the position to a variable-length item. |
| * |
| * The next call to VarBlockReaderGetNextItemPtr will |
| * get the specified item. |
| */ |
| void VarBlockReaderPosition( |
| VarBlockReader *varBlockReader, |
| int itemIndex) |
| { |
| VarBlockByteLen itemLen; |
| |
| Assert(varBlockReader != NULL); |
| Assert(itemIndex >= 0); |
| Assert(itemIndex < VarBlockGet_itemCount(varBlockReader->header)); |
| |
| VarBlockGetItemPtrAndLen(varBlockReader, itemIndex, |
| &varBlockReader->nextItemPtr, &itemLen); |
| |
| varBlockReader->nextIndex = itemIndex; |
| } |
| |
| static VarBlockByteLen VarBlockGetItemLen( |
| VarBlockReader *varBlockReader, |
| int itemIndex) |
| { |
| VarBlockHeader *header; |
| VarBlockByteLen itemLen; |
| VarBlockByteOffset offset; |
| |
| Assert(varBlockReader != NULL); |
| Assert(itemIndex >= 0); |
| |
| header = varBlockReader->header; |
| |
| offset = VarBlockGetOffset( |
| header, |
| varBlockReader->offsetToOffsetArray, |
| itemIndex); |
| if (itemIndex < VarBlockGet_itemCount(header) - 1) |
| { |
| VarBlockByteOffset nextOffset; |
| |
| nextOffset = VarBlockGetOffset( |
| header, |
| varBlockReader->offsetToOffsetArray, |
| itemIndex + 1); |
| |
| itemLen = nextOffset - offset; |
| Assert(itemLen >= 0); |
| } |
| else |
| { |
| Assert(itemIndex == VarBlockGet_itemCount(header) - 1); |
| |
| itemLen = |
| VARBLOCK_HEADER_LEN + |
| VarBlockGet_itemLenSum(header) - |
| offset; |
| Assert(itemLen >= 0); |
| } |
| |
| return itemLen; |
| } |
| |
| static void VarBlockGetItemPtrAndLen( |
| VarBlockReader *varBlockReader, |
| int itemIndex, |
| uint8 **itemPtr, |
| VarBlockByteLen *itemLen) |
| { |
| VarBlockHeader *header; |
| uint8 *buffer; |
| VarBlockByteOffset offset; |
| |
| Assert(varBlockReader != NULL); |
| |
| header = varBlockReader->header; |
| buffer = (uint8*)header; |
| |
| offset = VarBlockGetOffset( |
| header, |
| varBlockReader->offsetToOffsetArray, |
| itemIndex); |
| if (itemIndex < VarBlockGet_itemCount(header) - 1) |
| { |
| VarBlockByteOffset nextOffset; |
| |
| nextOffset = VarBlockGetOffset( |
| header, |
| varBlockReader->offsetToOffsetArray, |
| itemIndex + 1); |
| |
| *itemLen = nextOffset - offset; |
| Assert(*itemLen >= 0); |
| } |
| else |
| { |
| Assert(itemIndex == VarBlockGet_itemCount(header) - 1); |
| |
| *itemLen = |
| VARBLOCK_HEADER_LEN + |
| VarBlockGet_itemLenSum(header) - |
| offset; |
| Assert(*itemLen >= 0); |
| } |
| |
| *itemPtr = &buffer[offset]; |
| } |
| |
| |
| /* |
| * Get a pointer to the next variable-length item. |
| * |
| * Returns NULL when there are no more items. |
| */ |
| uint8* VarBlockReaderGetNextItemPtr( |
| VarBlockReader *varBlockReader, |
| VarBlockByteLen *itemLen) |
| { |
| uint8 *nextItemPtr; |
| |
| Assert(varBlockReader != NULL); |
| Assert(itemLen != NULL); |
| |
| if (varBlockReader->nextIndex >= |
| VarBlockGet_itemCount(varBlockReader->header)) |
| { |
| return NULL; |
| } |
| |
| *itemLen = VarBlockGetItemLen(varBlockReader, varBlockReader->nextIndex); |
| nextItemPtr = varBlockReader->nextItemPtr; |
| |
| varBlockReader->nextItemPtr += *itemLen; |
| varBlockReader->nextIndex++; |
| |
| return nextItemPtr; |
| } |
| |
| /* |
| * Get the variable-length item count. |
| */ |
| int VarBlockReaderItemCount( |
| VarBlockReader *varBlockReader) |
| { |
| Assert(varBlockReader != NULL); |
| |
| return VarBlockGet_itemCount(varBlockReader->header); |
| } |
| |
| /* |
| * Get a pointer to a variable-length item. |
| */ |
| uint8* VarBlockReaderGetItemPtr( |
| VarBlockReader *varBlockReader, |
| int itemIndex, |
| VarBlockByteLen *itemLen) |
| { |
| uint8 *nextItemPtr; |
| |
| Assert(varBlockReader != NULL); |
| Assert(itemIndex >= 0); |
| Assert(itemIndex < VarBlockGet_itemCount(varBlockReader->header)); |
| |
| VarBlockGetItemPtrAndLen(varBlockReader, itemIndex, &nextItemPtr, itemLen); |
| |
| return nextItemPtr; |
| } |
| |
| VarBlockByteLen VarBlockCollapseToSingleItem( |
| uint8 *target, |
| uint8 *source, |
| int32 sourceLen) |
| { |
| VarBlockReader varBlockReader; |
| uint8 *itemPtr; |
| VarBlockByteLen itemLen; |
| |
| VarBlockReaderInit( |
| &varBlockReader, |
| source, |
| sourceLen); |
| |
| Assert(VarBlockReaderItemCount(&varBlockReader) == 1); |
| |
| /* |
| * Copy out item, even if it destroys the VarBlock. |
| */ |
| itemPtr = VarBlockReaderGetItemPtr( |
| &varBlockReader, |
| /* itemIndex */ 0, |
| &itemLen); |
| /* |
| * Slide data back. Since we have overlapping data, |
| * we use memmove which knows how to do that instead of |
| * memcpy which isn't guaranteed to do that right. |
| */ |
| memmove(target, |
| itemPtr, |
| itemLen); |
| |
| return itemLen; |
| } |
| |