This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
deb-goldedplus/goldlib/gall/gutltag.cpp
2006-04-24 16:38:44 +00:00

323 lines
7.5 KiB
C++

// This may look like C code, but it is really -*- C++ -*-
// ------------------------------------------------------------------
// The Goldware Library
// Copyright (C) 1990-1999 Odinn Sorensen
// ------------------------------------------------------------------
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this program; if not, write to the Free
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
// MA 02111-1307, USA
// ------------------------------------------------------------------
// $Id$
// ------------------------------------------------------------------
// Tag number types and set class.
// ------------------------------------------------------------------
#include <cstdlib>
#include <cstring>
#include <gmemdbg.h>
#include <gutltag.h>
// ------------------------------------------------------------------
// Constructor.
GTag::GTag() {
granularity = 10;
tag = NULL;
allocated = tags = count = 0;
}
// ------------------------------------------------------------------
// Destructor.
GTag::~GTag() {
Reset();
}
// ------------------------------------------------------------------
// Deallocate and reset internal data.
void GTag::Reset() {
throw_xrelease(tag);
allocated = tags = 0;
// NOTE: Does and must NOT reset the count!
}
// ------------------------------------------------------------------
// Resize tag array.
// Returns NULL if realloc failed.
uint32_t* GTag::Resize(uint __tags) {
register uint newsize = 0;
if(__tags >= allocated)
newsize = __tags + granularity;
else if(__tags < allocated) {
if((allocated-__tags) > granularity)
newsize = __tags + granularity;
}
if(newsize) {
tag = (uint32_t*)throw_realloc(tag, newsize*sizeof(uint32_t));
allocated = newsize;
}
count = tags = __tags;
return tag;
}
// ------------------------------------------------------------------
// Appends a new tag number to the end of the tag array.
// NOTE - Does not check for duplicates or correct sequence!
uint32_t* GTag::Append(uint32_t __tagn) {
Resize(tags+1);
tag[tags-1] = __tagn;
return tag;
}
// ------------------------------------------------------------------
// Add a new tag number at the correct position of the tag array.
//
// Algorithm dry test situations:
//
// [] 0123456
// tag 345789x tags = 7 (after resize)
// _reln 1234567
//
// Case 1 - New tag is smaller than first tag in array:
// __tagn = 2
// _reln = 1 (changed to 0)
// tags-_reln-1 = 7-0-1 = 6
//
// Case 2 - Normal insert in middle:
// __tagn = 6
// _reln = 3
// tags-_reln-1 = 7-3-1 = 3
//
// Case 3 - New tag is larger than last tag in array:
// __tagn = 10
// _reln = 6
// tags-_reln-1 = 7-6-1 = 0
//
// Dry test result: Works!
uint32_t* GTag::Add(uint32_t __tagn) {
// Find closest tag number
uint _reln = ToReln(__tagn, TAGN_CLOSEST);
// Do we have it already?
if((_reln == RELN_INVALID) or (tag[_reln-1] != __tagn)) {
// Resize tag array to make room for the new number
Resize(tags+1);
// General rule:
// - tag[_reln-1] is smaller than __tagn
//
// The exceptions to the rule:
// - no tags in array (if _reln == RELN_INVALID)
// - tag[_reln-1] is larger than __tagn
if(_reln and (tag[_reln-1] > __tagn))
_reln--;
// Move data to make room for the new tag number
memmove(tag+_reln+1, tag+_reln, (tags-_reln-1)*sizeof(uint32_t));
// Copy the new tag number to the insert position
tag[_reln] = __tagn;
}
return tag;
}
// ------------------------------------------------------------------
// Delete a tag number from the tag array.
// Returns the relative tag number or RELN_INVALID if missing.
// NOTE - Does not resize the array.
uint GTag::Del(uint32_t __tagn) {
return DelReln(ToReln(__tagn));
}
// ------------------------------------------------------------------
uint GTag::DelReln(uint __reln) {
if(__reln) {
memmove(tag+__reln-1, tag+__reln, (tags-__reln)*sizeof(uint32_t));
count--;
tags--;
}
return __reln;
}
// ------------------------------------------------------------------
uint GTag::DelResize(uint32_t __tagn) {
uint _reln = Del(__tagn);
Resize(tags);
return _reln;
}
// ------------------------------------------------------------------
static int TagnCmp(const uint32_t* __a, const uint32_t* __b) {
return CmpV(*__a, *__b);
}
// ------------------------------------------------------------------
void GTag::Sort() {
qsort(tag, tags, sizeof(uint32_t), (StdCmpCP)TagnCmp);
}
// ------------------------------------------------------------------
void GTag::ElimDups() {
if(tags > 1) {
uint _before = tags;
uint32_t _last = tag[0];
for(uint n=1; n<tags; n++) {
if(_last == tag[n]) {
memmove(&tag[n-1], &tag[n], (tags-n)*sizeof(uint32_t));
tags--;
n--;
}
_last = tag[n];
}
if(_before != tags)
Resize(tags);
}
}
// ------------------------------------------------------------------
// Return the tag number corresponding to a relative tag number.
// Returns TAGN_INVALID, if the requested number did not exist.
uint32_t GTag::CvtReln(uint __reln) {
if(tag and __reln and (__reln <= tags))
return tag[__reln-1];
else
return TAGN_INVALID;
}
// ------------------------------------------------------------------
// Return the relative tag number corresponding to a tag number.
// Returns RELN_INVALID if the requested number did not exist.
// If __closest is true, the closest smaller tag number is returned.
// If no tags exist at all, RELN_INVALID is returned.
uint GTag::ToReln(uint32_t __tagn, int __closest) {
uint _lastreln = RELN_INVALID;
if(tag) {
if(__tagn and tags)
if(__tagn > tag[tags-1])
return __closest ? tags : RELN_INVALID;
if(tags and __tagn) {
int _mid;
int _left = 0;
int _right = tags;
do {
_mid = (_left+_right)/2;
if(__tagn < tag[(uint)_mid])
_right = _mid - 1;
else if(__tagn > tag[(uint)_mid])
_left = _mid + 1;
else
return (uint)(_mid + 1);
} while(_left < _right);
_lastreln = (uint)(_left + 1);
if(__tagn == tag[(uint)_left])
return _lastreln;
}
}
// Not found
return __closest ? _lastreln : RELN_INVALID;
}
// ------------------------------------------------------------------
uint GTag::ToReln(uint32_t __tagn) {
return ToReln(__tagn, TAGN_EXACT);
}
// ------------------------------------------------------------------
void GTag::Load(gfile& fp)
{
dword val;
fp.Fread(&val, sizeof(dword));
count = (uint) val;
if (count)
{
Resize(count);
fp.Fread(tag, sizeof(uint32_t), count);
}
}
// ------------------------------------------------------------------
void GTag::Save(gfile& fp)
{
dword val = (dword) count;
fp.Fwrite(&val, sizeof(dword));
if (tag and count)
fp.Fwrite(tag, sizeof(uint32_t), count);
}
// ------------------------------------------------------------------