323 lines
7.5 KiB
C++
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);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|