8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 20:03:03 +01:00
firebird-mirror/src/config/Stream.cpp

626 lines
13 KiB
C++
Raw Normal View History

2005-05-28 00:45:31 +02:00
/*
*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.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.ibphoenix.com/idpl.html.
*
* Software distributed under the License is distributed on
* an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the License for the specific
2005-05-28 00:45:31 +02:00
* language governing rights and limitations under the License.
*
* The contents of this file or any work derived from this file
* may not be distributed under any other license whatsoever
* without the express prior written permission of the original
2005-05-28 00:45:31 +02:00
* author.
*
*
* The Original Code was created by James A. Starkey for IBPhoenix.
*
* Copyright (c) 1997 - 2000, 2001, 2003 James A. Starkey
* Copyright (c) 1997 - 2000, 2001, 2003 Netfrastructure, Inc.
* All Rights Reserved.
*/
// Stream.cpp: implementation of the Stream class.
//
//////////////////////////////////////////////////////////////////////
#include <memory.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "firebird.h"
2006-06-16 14:36:09 +02:00
#include "../common/classes/alloc.h"
2005-05-28 00:45:31 +02:00
#include "Stream.h"
#include "StreamSegment.h"
// CVC: The logic in this module seems to overwrite constant params passed into the
// address field of the Segment structure. This includes literal strings (see Element.cpp).
2005-05-28 00:45:31 +02:00
#ifndef MAX
#define MAX(a,b) ((a > b) ? a : b)
#define MIN(a,b) ((a < b) ? a : b)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
2005-05-28 00:45:31 +02:00
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Stream::Stream(int minSegmentSize)
{
segments = NULL;
current = NULL;
totalLength = 0;
minSegment = minSegmentSize;
copyFlag = true;
}
Stream::~Stream()
{
clear();
}
void Stream::putCharacter(char c)
{
if (!segments || current->length >= currentLength)
allocSegment (MAX (100, minSegment));
current->address [current->length] = c;
++current->length;
++totalLength;
}
void Stream::putSegment(int length, const char *ptr, bool copy)
{
#ifdef ENGINE
ASSERT (length >= 0);
#endif
if (length == 0)
return;
2008-04-19 11:40:19 +02:00
const char *address = ptr;
2005-05-28 00:45:31 +02:00
totalLength += length;
if (!segments)
{
2005-05-28 00:45:31 +02:00
if (copyFlag = copy)
{
2005-05-28 00:45:31 +02:00
allocSegment (MAX (length, minSegment));
current->length = length;
memcpy (current->address, address, length);
}
2005-05-28 00:45:31 +02:00
else
{
2005-05-28 00:45:31 +02:00
//copyFlag = copy;
current = segments = &first;
current->length = length;
current->address = (char*) address;
current->next = NULL;
}
}
2005-05-28 00:45:31 +02:00
else if (copyFlag)
{
2005-05-28 00:45:31 +02:00
int l = currentLength - current->length;
if (l > 0)
{
const int l2 = MIN (l, length);
2005-05-28 00:45:31 +02:00
memcpy (current->address + current->length, address, l2);
current->length += l2;
length -= l2;
address += l2;
}
2005-05-28 00:45:31 +02:00
if (length)
{
2005-05-28 00:45:31 +02:00
allocSegment (MAX (length, minSegment));
current->length = length;
memcpy (current->address, address, length);
}
}
2005-05-28 00:45:31 +02:00
else
{
2005-05-28 00:45:31 +02:00
allocSegment (0);
current->address = (char*) address;
current->length = length;
}
2005-05-28 00:45:31 +02:00
}
int Stream::getSegment(int offset, int len, void* ptr) const
2005-05-28 00:45:31 +02:00
{
int n = 0;
int length = len;
char* address = static_cast<char*>(ptr);
2005-05-28 00:45:31 +02:00
2008-04-19 11:40:19 +02:00
for (const Segment *segment = segments; segment; n += segment->length, segment = segment->next)
{
2005-05-28 00:45:31 +02:00
if (n + segment->length >= offset)
2008-04-19 11:40:19 +02:00
{
const int off = offset - n;
const int l = MIN (length, segment->length - off);
2005-05-28 00:45:31 +02:00
memcpy (address, segment->address + off, l);
address += l;
length -= l;
offset += l;
if (!length)
break;
2008-04-19 11:40:19 +02:00
}
}
2005-05-28 00:45:31 +02:00
return len - length;
}
void Stream::setSegment(Segment * segment, int length, void* address)
{
segment->length = length;
totalLength += length;
if (copyFlag)
{
2005-05-28 00:45:31 +02:00
segment->address = new char [length];
memcpy (segment->address, address, length);
}
2005-05-28 00:45:31 +02:00
else
segment->address = (char*) address;
}
void Stream::setMinSegment(int length)
{
minSegment = length;
}
Stream::Segment* Stream::allocSegment(int tail)
2005-05-28 00:45:31 +02:00
{
fb_assert(tail >= 0);
2005-05-28 00:45:31 +02:00
Segment *segment;
int length = tail;
if (!current && tail <= FIXED_SEGMENT_SIZE)
{
2005-05-28 00:45:31 +02:00
segment = &first;
length = FIXED_SEGMENT_SIZE;
}
2005-05-28 00:45:31 +02:00
else
{
// CVC: What do we do here in the release build if tail is smaller than FIXED_SEGMENT_SIZE?
fb_assert(tail >= FIXED_SEGMENT_SIZE);
segment = (Segment*) new char [sizeof(Segment) + tail - FIXED_SEGMENT_SIZE];
}
2005-05-28 00:45:31 +02:00
segment->address = segment->tail;
segment->next = NULL;
segment->length = 0;
currentLength = length;
if (current)
{
2005-05-28 00:45:31 +02:00
current->next = segment;
current = segment;
}
2005-05-28 00:45:31 +02:00
else
segments = current = segment;
return segment;
}
2008-04-19 11:40:19 +02:00
int Stream::getSegment(int offset, int len, void * ptr, char delimiter) const
2005-05-28 00:45:31 +02:00
{
int n = 0;
int length = len;
char *address = static_cast<char*>(ptr);
2005-05-28 00:45:31 +02:00
2008-04-19 11:40:19 +02:00
for (const Segment *segment = segments; segment; n += segment->length, segment = segment->next)
{
2005-05-28 00:45:31 +02:00
if (n + segment->length >= offset)
2008-04-19 11:40:19 +02:00
{
const int off = offset - n;
const int l = MIN (length, segment->length - off);
const char *p = segment->address + off;
for (const char *end = p + l; p < end;)
{
2008-04-19 11:40:19 +02:00
const char c = *address++ = *p++;
2005-05-28 00:45:31 +02:00
--length;
if (c == delimiter)
return len - length;
}
2005-05-28 00:45:31 +02:00
if (!length)
break;
2008-04-19 11:40:19 +02:00
}
}
2005-05-28 00:45:31 +02:00
return len - length;
}
void Stream::putSegment(const char * string)
{
if (string [0])
putSegment ((int) strlen (string), string, true);
}
char* Stream::getString()
{
2008-04-19 11:40:19 +02:00
char* const string = new char [totalLength + 1];
2005-05-28 00:45:31 +02:00
getSegment (0, totalLength, string);
string [totalLength] = 0;
return string;
}
#ifdef ENGINE
2008-04-19 11:40:19 +02:00
void Stream::compress(int length, const void * address)
2005-05-28 00:45:31 +02:00
{
//printShorts ("Original data", (length + 1) / 2, (short*) address);
Segment *segment = allocSegment (length + 5);
short *q = (short*) segment->address;
2008-04-19 11:40:19 +02:00
const short *p = (short*) address;
const short* const end = p + (length + 1) / 2;
2008-04-19 11:40:19 +02:00
const short* const yellow = end - 2;
2005-05-28 00:45:31 +02:00
*q++ = length;
while (p < end)
{
2005-05-28 00:45:31 +02:00
short *start = ++q;
while (p < end && (p >= yellow || p[0] != p[1] || p[1] != p[2]))
2005-05-28 00:45:31 +02:00
*q++ = *p++;
int n = q - start;
if (n)
start [-1] = -n;
else
--q;
if (p >= end)
break;
2008-04-19 11:40:19 +02:00
const short* start2 = p++;
while (p < end && *p == *start2)
2005-05-28 00:45:31 +02:00
++p;
2008-04-19 11:40:19 +02:00
n = p - start2;
2005-05-28 00:45:31 +02:00
*q++ = n;
2008-04-19 11:40:19 +02:00
*q++ = *start2;
}
2005-05-28 00:45:31 +02:00
totalLength = segment->length = (char*) q - segment->address;
}
char* Stream::decompress(int tableId, int recordNumber)
{
char *data;
short *q, *limit;
int run = 0;
decompressedLength = 0;
for (Segment *segment = segments; segment; segment = segment->next)
{
2005-05-28 00:45:31 +02:00
if (segment->length == 0)
continue;
const short* p = (short*) segment->address;
const short* end = (short*) (segment->address + segment->length);
2005-05-28 00:45:31 +02:00
if (decompressedLength == 0)
{
2005-05-28 00:45:31 +02:00
decompressedLength = *p++;
if (decompressedLength <= 0)
{
Log::log ("corrupted record, table %d, record %d, decompressed length %d\n",
2005-05-28 00:45:31 +02:00
tableId, recordNumber, decompressedLength);
throw SQLEXCEPTION (RUNTIME_ERROR, "corrupted record, table %d, record %d, decompressed length %d",
2005-05-28 00:45:31 +02:00
tableId, recordNumber, decompressedLength);
}
int len = ((decompressedLength + 1) / 2) * 2;
2005-05-28 00:45:31 +02:00
//data = new char [len];
data = ALLOCATE_RECORD (len);
limit = (short*) (data + decompressedLength);
if (decompressedLength & 1)
++limit;
q = (short*) data;
}
2005-05-28 00:45:31 +02:00
while (p < end)
{
const short n = *p++;
2005-05-28 00:45:31 +02:00
//#ifdef ENGINE
if (n == 0 && run == 0)
{
2005-05-28 00:45:31 +02:00
Log::log ("corrupted record (zero run), table %d, record %d\n", tableId, recordNumber);
printShorts ("Zero run", (segment->length + 1) / 2, (short*) segment->address);
2005-05-28 00:45:31 +02:00
printChars ("Zero run", segment->length, segment->address);
}
2005-05-28 00:45:31 +02:00
//#endif
if (run > 0)
{
2005-05-28 00:45:31 +02:00
for (; run; --run)
*q++ = n;
}
2005-05-28 00:45:31 +02:00
else if (run < 0)
{
2005-05-28 00:45:31 +02:00
*q++ = n;
++run;
}
2005-05-28 00:45:31 +02:00
else
{
2005-05-28 00:45:31 +02:00
run = n;
if (q + run > limit)
{
2005-05-28 00:45:31 +02:00
//#ifdef ENGINE
Log::log ("corrupted record (overrun), table %d, record %d\n", tableId, recordNumber);
printShorts ("Compressed", (segment->length + 1)/2, (short*) segment->address);
printChars ("Compressed", segment->length, segment->address);
//#endif
if (q == limit)
return data;
throw SQLEXCEPTION (RUNTIME_ERROR, "corrupted record, table %d, record %d", tableId, recordNumber);
}
}
}
}
//printShorts ("Decompressed", (decompressedLength + 1) / 2, (short*) data);
2005-05-28 00:45:31 +02:00
return data;
}
void Stream::printShorts(const char * msg, int length, short * data)
{
Log::debug ("%s", msg);
for (int n = 0; n < length; ++n)
{
2005-05-28 00:45:31 +02:00
if (n % 10 == 0)
Log::debug ("\n ");
Log::debug ("%d, ", data [n]);
}
2005-05-28 00:45:31 +02:00
Log::debug ("\n");
}
void Stream::printChars(const char * msg, int length, const char * data)
{
Log::debug ("%s", msg);
for (int n = 0; n < length; ++n)
{
2005-05-28 00:45:31 +02:00
if (n % 50 == 0)
Log::debug ("\n ");
const char c = data [n];
2005-05-28 00:45:31 +02:00
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
putchar (c);
else
putchar ('.');
}
2005-05-28 00:45:31 +02:00
Log::debug ("\n");
}
#endif
void Stream::clear()
{
Segment *segment;
while (segment = segments)
{
2005-05-28 00:45:31 +02:00
segments = segment->next;
if (segment != &first)
delete[] (char*) segment;
}
2005-05-28 00:45:31 +02:00
current = NULL;
totalLength = 0;
}
void Stream::putSegment(int length, const unsigned short *chars)
{
if (length == 0)
return;
totalLength += length;
const unsigned short *wc = chars;
if (!segments)
{
2005-05-28 00:45:31 +02:00
allocSegment (MAX (length, minSegment));
current->length = length;
}
2005-05-28 00:45:31 +02:00
else
{
2005-05-28 00:45:31 +02:00
int l = currentLength - current->length;
if (l > 0)
{
const int l2 = MIN (l, length);
2005-05-28 00:45:31 +02:00
char *p = current->address + current->length;
for (int n = 0; n < l2; ++n)
*p++ = (char) *wc++;
//memcpy (current->address + current->length, address, l2);
current->length += l2;
length -= l2;
//address += l2;
}
2005-05-28 00:45:31 +02:00
if (length)
{
2005-05-28 00:45:31 +02:00
allocSegment (MAX (length, minSegment));
current->length = length;
//memcpy (current->address, address, length);
}
}
2005-05-28 00:45:31 +02:00
char *p = current->address;
for (int n = 0; n < length; ++n)
*p++ = (char) *wc++;
}
2008-04-19 11:40:19 +02:00
int Stream::getLength() const
2005-05-28 00:45:31 +02:00
{
return totalLength;
}
2008-04-19 11:40:19 +02:00
int Stream::getSegmentLength(int offset) const
2005-05-28 00:45:31 +02:00
{
int n = 0;
2008-04-19 11:40:19 +02:00
for (const Segment *segment = segments; segment; segment = segment->next)
{
2005-05-28 00:45:31 +02:00
if (offset >= n && offset < n + segment->length)
return n + segment->length - offset;
n += segment->length;
}
2005-05-28 00:45:31 +02:00
return 0;
}
void* Stream::getSegment(int offset)
{
int n = 0;
for (Segment *segment = segments; segment; segment = segment->next)
{
2005-05-28 00:45:31 +02:00
if (offset >= n && offset < n + segment->length)
return segment->address + offset - n;
n += segment->length;
}
2005-05-28 00:45:31 +02:00
return NULL;
}
2008-04-19 11:40:19 +02:00
const void* Stream::getSegment(int offset) const
{
int n = 0;
for (const Segment *segment = segments; segment; segment = segment->next)
{
2008-04-19 11:40:19 +02:00
if (offset >= n && offset < n + segment->length)
return segment->address + offset - n;
n += segment->length;
}
2008-04-19 11:40:19 +02:00
return NULL;
}
2005-05-28 00:45:31 +02:00
void Stream::putSegment(Stream * stream)
{
/***
for (Segment *segment = stream->segments; segment; segment = segment->next)
putSegment (segment->length, segment->address, true);
***/
if (stream->totalLength == 0)
return;
StreamSegment seg(stream);
2005-05-28 00:45:31 +02:00
if (current)
{
2005-05-28 00:45:31 +02:00
for (int len = currentLength - current->length; len && seg.available;)
{
2008-04-19 11:40:19 +02:00
const int l = MIN (len, seg.available);
2005-05-28 00:45:31 +02:00
putSegment (l, seg.data, true);
seg.advance (l);
len -= l;
}
}
2005-05-28 00:45:31 +02:00
if (seg.remaining)
seg.copy (alloc (seg.remaining), seg.remaining);
}
Firebird::string Stream::getFBString() const
2005-05-28 00:45:31 +02:00
{
Firebird::string string;
2005-05-28 00:45:31 +02:00
char *p = string.getBuffer (totalLength);
for (const Segment *segment = segments; segment; segment = segment->next)
{
2005-05-28 00:45:31 +02:00
memcpy (p, segment->address, segment->length);
p += segment->length;
}
fb_assert(p - string.begin() == totalLength);
2005-05-28 00:45:31 +02:00
return string;
}
char* Stream::alloc(int length)
{
totalLength += length;
if (!current || length > currentLength - current->length)
allocSegment (length);
char* const p = current->tail + current->length;
2005-05-28 00:45:31 +02:00
current->length += length;
return p;
}
void Stream::format(const char *pattern, ...)
{
Firebird::string temp;
2005-05-28 00:45:31 +02:00
va_list args;
va_start (args, pattern);
temp.vprintf(pattern, args);
va_end(args);
putSegment (temp.c_str());
2005-05-28 00:45:31 +02:00
}
void Stream::truncate(int length)
{
int n = 0;
for (Segment *segment = segments; segment; segment = segment->next)
{
2005-05-28 00:45:31 +02:00
if (length >= n && length < n + segment->length)
{
2005-05-28 00:45:31 +02:00
current = segment;
current->length = length - n;
totalLength = length;
while (segment = current->next)
{
2005-05-28 00:45:31 +02:00
current->next = segment->next;
delete[] (char*) segment; // Was deallocation bug
2005-05-28 00:45:31 +02:00
}
return;
2005-05-28 00:45:31 +02:00
}
n += segment->length;
}
2005-05-28 00:45:31 +02:00
}
2008-04-19 11:40:19 +02:00
int Stream::compare(const Stream *stream) const
2005-05-28 00:45:31 +02:00
{
for (int offset = 0;;)
{
const int length1 = getSegmentLength(offset);
const int length2 = stream->getSegmentLength(offset);
2005-05-28 00:45:31 +02:00
if (length1 == 0)
2008-04-19 11:40:19 +02:00
{
2005-05-28 00:45:31 +02:00
if (length2)
return -1;
2008-04-19 11:40:19 +02:00
return 0;
}
2005-05-28 00:45:31 +02:00
if (length2 == 0)
return 1;
2008-04-19 11:40:19 +02:00
const int length = MIN (length1, length2);
2005-05-28 00:45:31 +02:00
const char *p1 = (const char*) getSegment (offset);
const char *p2 = (const char*) stream->getSegment (offset);
for (const char *end = p1 + length; p1 < end;)
{
2005-05-28 00:45:31 +02:00
int n = *p1++ - *p2++;
if (n)
return n;
}
offset += length;
}
2005-05-28 00:45:31 +02:00
}