Files
Tools_CPP/lib/tiptopbd.h
2025-10-04 11:42:17 +05:00

492 lines
26 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//---------------------------------------------------------------------------
#ifndef TIPTOPBD_H_
#define TIPTOPBD_H_
//---------------------------------------------------------------------------
#include <math.h>
#include <wx/string.h>
#include <wx/wfstream.h>
#include <wx/mstream.h> //Поток в память
#include <wx/stream.h> //Поток буф
#include "structs.h"
#include "tcDebug.h"
//---------------------------------------------------------------------------
typedef unsigned char uint1;
typedef unsigned short uint2;
typedef unsigned int uint4;
typedef unsigned __int64 uint8;
typedef char int1;
typedef short int2;
typedef int int4;
typedef __int64 int8;
//---------------------------------------------------------------------------
const uint2 file_id=65500;//*((uint2*)"TB");//16980 id файла базы данных и таблицы 2 байта
const uint2 file_version=1; //текущая версия файла базы данных 2 байта
//---------------------------------------------------------------------------
//типы данных которые можно хранить в базе под них 1 байт
#define BD_UINT1_B 27 //1 байт без знаковый
#define BD_UINT1 0 //1 байт без знаковый
#define BD_UINT2 1 //2 байт без знаковый
//#define BD_UINT3 2 //3 байт без знаковый
#define BD_UINT4 3 //4 байт без знаковый
//#define BD_UINT5 4 //5 байт без знаковый
//#define BD_UINT6 5 //6 байт без знаковый
//#define BD_UINT7 6 //7 байт без знаковый
#define BD_UINT8 7 //8 байт без знаковый
#define BD_INT1 10 //1 байт со знаковый
#define BD_INT2 11 //2 байт со знаковый
//#define BD_INT3 12 //3 байт со знаковый
#define BD_INT4 13 //4 байт со знаковый
//#define BD_INT5 14 //5 байт со знаковый
//#define BD_INT6 15 //6 байт со знаковый
//#define BD_INT7 16 //7 байт со знаковый
#define BD_INT8 17 //8 байт со знаковый
#define BD_FLOAT4 20 //17 - float 4_байта
//#define BD_FLOAT6 21 //18 - real 6_байт
#define BD_FLOAT8 22 //19 - double 8_байт
#define BD_BOOL 30 //1 байт 0-false 1-true
#define BD_UTF8_1 100 //100 - utf8_1 string 1й байт размер строки в байтах
#define BD_UTF8_2 101 //101 - utf8_2 string 1х 2 байта размер строки в байтах
//#define BD_UTF8_4 102 //101 - utf8_2 string 1х 4 байта размер строки в байтах
//#define BD_ASCII_1 110 //102 - asci_1 строка 1й байт размер строки в байтах
//#define BD_ASCII_2 112 //103 - asci_2 строка 1х 2 байта размер строки в байтах
//-#define BD_ASCII_0 120 //Строка с 0 на конце
//для этих двоичных объектов в начале передаются не данные а только размер потом обновляют запись по id записи
#define BD_GPS_4 130 //250 - GPS коордионаты в формате: uint4 количество байт,float,float (широта lat,долгота lon в WGS-84)
#define BD_GPS_8 131 //251 - GPS коордионаты в формате: uint4 количество байт, double,double (широта lat,долгота lon в WGS-84 )
#define BD_GPSS_8 132 //252 - GPS координаты в формате: uint4 количество байт, uint4 количество сегментов, uint4 количество точек в первом сегменте,(double,double точки первого сегмента)
//#define BD_BLOB_1 140 //254 - двоичные данные uint1 количество байт
//#define BD_BLOB_2 141 //254 - двоичные данные uint2 количество байт
//#define BD_BLOB_3 142 //254 - двоичные данные uint3 количество байт
#define BD_BLOB_4 143 //254 - двоичные данные uint4 количество байт
//#define BD_BLOB_8 144 //255 - двоичные данные uint8 количество байт
//---------------------------------------------------------------------------
//"Типа" свой поток для записи в память либо в файл
class TiptopStream
{
private:
uint4 m_posRead; //Текущая позиция при записи
uint4 m_posWrite; //Текущая позиция при чтении
uint4 m_size; //Размер данных
//список страниц базы данных
unsigned int m_pageSize; //Размер страницы данных
TSimpleList<unsigned char*>* m_pages; //список страниц данных
public:
TiptopStream(){ m_pageSize=1024;/*4096;*/ m_pages=new TSimpleList<unsigned char*>(10,true); m_posRead=0; m_posWrite=0; m_size=0; };
~TiptopStream(){ delete m_pages; };
void Clear()
{ m_pages->clear();
}
uint4 GetSize(){ return m_size; };
void Read(const void* buf, unsigned int size) //Прочитать данные в buffer размером
{
unsigned char* buffer=(unsigned char*)buf;
while(size>0)
{
unsigned int b=(m_posRead / (float)m_pageSize); //Блок
unsigned int p=m_posRead - b * m_pageSize; //Позиция в блоке
unsigned char* ch=m_pages->get(b) + p; //Буфер на нужной позиции
unsigned int s = m_pageSize - p; //сколько можно и нужно прочитать из блока
if(s > size) s=size;
memcpy(buffer, ch, s);
buffer+=s;
size-=s;
m_posRead+=s;
}
};
void Read(wxOutputStream* os) //Записать все данные в os
{
uint4 s=m_size;
for(uint4 i=0;i<m_pages->count();i++)
{
unsigned char* ch=m_pages->get(i);
if(s>m_pageSize)
{ s-=m_pageSize;
os->Write(ch,m_pageSize);
}
else os->Write(ch,s);
}
}
void Write(const void* buf, unsigned int size)
{
unsigned char* buffer=(unsigned char*)buf;
if(m_posWrite+size>m_pages->count()*m_pageSize) //Расширить память
{
unsigned int s=ceil((m_posWrite+size) / (float)m_pageSize);
if(s>m_pages->count())
{ s-=m_pages->count();
for(unsigned int i=0;i<s;i++) m_pages->add( new unsigned char[m_pageSize] );
}
}
while(size>0)
{
unsigned int b=floor(m_posWrite / (float)m_pageSize); //Находим нужный блок памяти
unsigned int p=m_posWrite - b * m_pageSize; //Позиция с начала блока памяти
unsigned char* ch=m_pages->get(b) + p;
unsigned int s = m_pageSize - p;
if(s > size) s=size;
memcpy(ch, buffer, s);
buffer+=s;
size-=s;
m_posWrite+=s;
}
if(m_size<m_posWrite+size) m_size=m_posWrite+size;
};
void Write(wxInputStream* s)
{
unsigned char* buf=new unsigned char[1024];
while(true)
{
s->Read(buf,1024);
unsigned int r=s->LastRead();
if(r==0) break;
Write(buf,r);
}
delete buf;
};
void SeekRead(uint4 pos){ m_posRead=pos; };
uint4 TellRead(){ return m_posRead; };
void SeekWrite(uint4 pos) { m_posWrite=pos; }
uint4 TellWrite(){ return m_posWrite; };
};
//---------------------------------------------------------------------------
//Преобразовать значние в соответствии с типом если int и пустая строка то NULL
wxString getSQLValue(wxString t,wxString v);
//---------------------------------------------------------------------------
class TiptopTable;
//---------------------------------------------------------------------------
/*
//http://slady.net/java/bt/view.php?w=1024&h=768
//узел двоичного дерева фиксированной длины (высчитать размер надо чтоб с диска читалось примерно по 512 байта)
//По определению, двоичное дерево называется АВЛ деревом в том и только в том случае, когда высоты двух поддеревьев каждой из вершин дерева отличаются не более, чем на единицу
class BTreeNode
{
private:
public:
int1 balance; //Баланс для АВЛ дерева -1 - левое длиней, 0 - сбалансированно, 1 - правое длиней
uint4 val; //размеры свободного пространства
uint4 pos; //позиции в файле для размера
uint4 fllink,frlink; //ссылки на дочерние узлы в файле
BTreeNode *llink,*rlink; //ссылки на дочерние узлы в памяти
BTreeNode *parent; //родительмкий узел дла нормализации
BTreeNode(){llink=NULL; rlink=NULL;};
~BTreeNode(){};
};
//Индекс пустых мест в таблице создаётся для каждой таблицы (потом наверно и для всего файла надо будет сделать)
//отсортированно по возрастанию свободного места "кол-во места->позиция в файле"
/*class TiptopIndexFreeSpase
{
private:
// TiptopTable* m_table;
uint4 m_count; //количество элементов
wxFFile* m_file; //чтение и запись
bool LLRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2); //Одинарный LL-поворот.
bool RRRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2); //Одинарный RR-поворот.
bool LRRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2); //Двойной LR-поворот.
bool RLRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2); //Двойной RL-поворот.
public:
TiptopIndexFreeSpase(TiptopTable* table);
~TiptopIndexFreeSpase();
uint4 findFree(uint4 size); //найти позицию в файле с минимальным подходящим размером для записи
bool setUseSpace(uint4 pos,uint4 size); //сообщить что данная позиция занята
bool setFreeSpase(uint4 pos,uint4 size); //сообщить что данные освободились
void updateLast(); //обновить размер по данным поледнего вызова findFree
};
//Есть 4 варианта нарушения балансировки АВЛ-дерева
//Одинарный LL-поворот. Выполняется, когда <перевес> идет по пути L-L от узла с нарушенной балансировкой.
bool TiptopIndexFreeSpase::LLRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2)
{
p1 := p^.llink;
p^.llink := p1^.rlink;
p1^.rlink := p;
p := p1;
}
//Одинарный RR-поворот. Выполняется, когда <перевес> идет по пути R-R от узла с нарушенной балансировкой.
bool TiptopIndexFreeSpase::RRRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2)
{
p1 := p^.rlink;
p^.rlink := p1^.llink;
p1^.llink := p;
p := p1;
}
//Двойной LR-поворот. Выполняется, когда <перевес> идет по пути L-R от узла с нарушенной балансировкой.
bool TiptopIndexFreeSpase::LRRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2)
{
p1 := p^.llink;
p2 := p1^.rlink;
p1^.rlink := p2^.llink;
p2^.llink := p1;
p^.llink := p2^.rlink;
p2^.rlink := p;
p := p2;
}
//Двойной RL-поворот. Выполняется, когда <перевес> идет по пути R-L от узла с нарушенной балансировкой.
bool TiptopIndexFreeSpase::RLRotation(BTreeNode* p0,BTreeNode* p1,BTreeNode* p2)
{
p1 := p^.rlink;
p2 := p1^.llink;
p1^.llink := p2^.rlink;
p2^.rlink := p1;
p^.rlink := p2^.llink;
p2^.llink := p;
p := p2;
}
*/
//---------------------------------------------------------------------------
class TiptopField;
//---------------------------------------------------------------------------
//числовой индекс базы данных (для id поля он обязателен)
//Создаётся файл вида "id таблицы_id поля.i" (если id поля = 0 то это индекс свободных мест)
class TiptopIndex
{
private:
uint4 m_type; //Тип индекса (1 - самый простой)
uint4 m_count; //количество элементов
wxFFile* m_file; //чтение и запись
public:
TiptopField* m_fl; //Поле таблицы для которого строится индекс
TiptopIndex(TiptopField* fl);
~TiptopIndex();
void add(uint4 pos,uint4 id);
bool getPos(uint4 val,uint4& pos,int4& num); //Поиск значения среди упорядоченного списка
};
//---------------------------------------------------------------------------
struct RuiPointPS
{ unsigned int pos; //позиция в файле
unsigned int size; //Размер
};
//---------------------------------------------------------------------------
//Индекс пустых мест пока как таблица (потом переделать в двоичный список)
class TiptopIndexFreeSize
{
private:
TSimpleList<RuiPointPS*>* m_Pos; ///<Массив позиции
public:
TiptopIndexFreeSize()
{ m_Pos=new TSimpleList<RuiPointPS*>(10,true);
};
~TiptopIndexFreeSize(){ delete m_Pos; };
void add(unsigned int pos, unsigned int size) //Добавить "пустое место" в список
{ RuiPointPS* p=new RuiPointPS;
p->pos=pos; p->size=size;
m_Pos->add(p);
};
bool del(unsigned int pos) //Удалить из списка свободных мест
{ for(uint4 i=0;i<m_Pos->count();i++)
if(m_Pos->get(i)->pos==pos) { return m_Pos->del(i); }
return false;
};
bool upd(unsigned int pos, unsigned int size) //Сдвинуть позицию на заданый размер и уменьшить размер
{
for(uint4 i=0;i<m_Pos->count();i++)
{
if(m_Pos->get(i)->pos==pos)
{
unsigned int s=m_Pos->get(i)->size;
if(s==size)
{ m_Pos->del(i);
break;
}
if(s<size) return false;
else
{ m_Pos->get(i)->pos+=size;
m_Pos->get(i)->size-=size;
}
break;
}
}
return false;
};
bool get(unsigned int size,unsigned int &pos) //Получить позицию подходящую под заданный размер
{ bool b=false;
unsigned int min=0xffffffff;
for(unsigned int i=0;i<m_Pos->count();i++)
{
if(m_Pos->get(i)->size>=size && m_Pos->get(i)->size<min)
{ pos=m_Pos->get(i)->pos;
min=m_Pos->get(i)->size;
b=true;
}
}
return b;
};
};
//---------------------------------------------------------------------------
class TiptopTable;
//---------------------------------------------------------------------------
//поле базы данных
class TiptopField
{
private:
//Для 1 го поля 1н индекс
public:
TiptopTable* m_tb; //Если NULL то поле не присоединено к таблице
void* m_value; //ссылка на данные (может быть NULL)
uint4 m_size; //размер данных (Если 0 то в базе NULL)
wxString m_name; //название поля
uint1 m_type_id; //id типа данных
bool m_NULL; //может ли быть пустым
bool m_index; //Нужен ли индекс для этого поля (не используется, может быть: первичным, вторичным, уникальным, просто индексом для поиска)
TiptopIndex* m_idx; //Индекс поля (пока только для поля id)
TiptopField(TiptopTable* tb); //Конструктор задаётся какой таблице принадлежит
~TiptopField();
void setValue(void* value,uint4 size); //Данные копируются
void setValue(TiptopStream* is,uint4 size); //Записать данные
void setValue(int value);
void setValue(wxString value); //Прочитать данные из строки в соответствии с типом
void setValue(bool value); //Присвоить bool значение
void setValue(double value); //Присвоить double значение
TiptopField* cloneNode(); ///< Клонировать узел
uint4 getSize()
{ if(m_value==NULL) return 0;
return m_size;
}; ///<Размер без учёта размера под данные
uint4 getAllSize(); ///<Размер с учётом типа поля
//Функции для взятия значений
bool getBoolVal();
uint4 getUint4Val();
int4 getIntVal();
double getDblVal();
wxString getStrVal();
wxString getStrSQLVal();
};
//---------------------------------------------------------------------------
class TiptopBD;
//---------------------------------------------------------------------------
//таблица данных может быть присоединённой к данным а может и отдельно волятся...
//Если отдельно то для неё не предназначенны вторичные ключи только 1 первичный и индексы...
class TiptopTable
{
private:
uint1 m_Type; //Тип таблицы 0-Плотная(Индекс не обязателен последовательный доступ) 1-Жидкая (Индекс обязателен)
//wxOutputStream* m_os; ///<Для записи заголовка и данных данных
//wxMemoryOutputStream* m_os; ///<Для записи заголовка и данных
//bool m_osOwner; ///<Владелец ли m_os смотри деструктор
//bool m_ok; //выполнилась ли последняя операция успешно
//wxString m_path; //Путь к файлу таблицы
int4 getNumFieldOnName(wxString name); //вернуть позицию поля по имени нумерация с 0, если -1 то нет такого
TSimpleList2<uint8> m_Pos; ///<Массив позиции полей от начала файла либо потока
TiptopIndexFreeSize* m_IndexFreeSize; //Индекс свободного пространства
bool buildPosIndex(); ///<Построить индекс позиций прочитав таблацу он начала до конца
public:
//wxFileOutputStream* m_fos; //для записи в файл таблицы
//wxFileInputStream* m_fis; //для чтения из файла таблицы
//wxInputStream* m_is; ///<Для чтения заголовка таблицы и данных
wxFFile* m_file; //чтение и запись
TiptopStream* m_stream; ///<С этим потоком работает таблица для чтения и записи
uint1* m_Null;//Массив байт для чтения и записи NULL значений (равно количеству столбцов не зависимо возможно ли NULL)
uint1 m_NullSize;//количество байт для NULL, подсчитываются = cell(count fields/8)
uint4 m_RecSize; ///<Размер последней прочитанной записи в байтах с учётом null и типов
uint4 m_RecPos; ///<Позиция последней прочитаной записи
TSimpleList<TiptopField*>* fields; //список столбцов владелец (идут попорядку id поля нет есть позиция)
TiptopBD* m_bd; //Если NULL то таблица не присоединена к базе данных
uint4 m_id; //id таблицы в нутри базы (или запроса)
wxString m_name; //Название таблицы (читается из файла)
int m_count; //Количество записей (заранее неизвестно если нет индекса)
//uint4 rCount; //Количество записей в таблице (Информационное поле)
TiptopTable(TiptopBD* bd,uint4 id);
~TiptopTable();
//bool isOk();
bool AddField(TiptopField* fl); ///<Добавить поле к таблице становится владельцем (без записи в поток)
TiptopField* AddField(wxString name,uint1 type_id); ///<Добавить поле к таблице (без записи в поток)
TiptopField* AddField(wxString name,wxString type); ///<Добавить поле к таблице (без записи в поток)
bool OpenTable(); //открыть таблицу (если обьект TiptopTable не в базе если таблица не открылась то результат false а путь к файлу сохраниться)
//bool OpenTable(wxInputStream* is); //Подгрузить таблицу из потока (Читает только заголовок)
bool OpenTable(wxString path); //Прочитать из файла
// bool SetOutputStream(wxMemoryOutputStream* os, bool owner=false);
// const wxMemoryOutputStream* GetOutputStream(){return m_os;};
bool ReadTableToMem(wxInputStream* is); ///<Прочитать таблицу из потока в оперативную память
bool CreateTable(wxString SQL); //Создать файл таблицы через SQL запрос
bool InsertInto(wxString SQL); //Добавить запись в таблицу и в индексы через SQL запрос
bool UpdateTable(wxString SQL); //"простое" обновление записей через SQL запрос
wxFFile* getStream(uint4 id,wxString field); //TODO надо сделать свой защищённый "поток"
//wxMemoryInputStream* GetInputStream(); ///<Создаёт новый поток в который помещяет таблицу с данными для чтения (TiptopTable не остаётся владельцем)
TiptopField* getField(wxString name); //Получить поле по имени (Для того чтобы получить по индексу используй TSimpleList)
uint4 findFreeSize(uint4 size = 0); //Найти позицию с свободным местом для записи данных (из индекса свободных мест)
bool FindFirstRecord(TiptopField* fl,uint4 &pos,uint4 &num); ///<Найти позицию первой попавшейся записи с заданными значениями поля, результат позиция записи в памяти.
bool SeekToStart(); //Переместить курсор чтения на первую запись (чтоб потом можно было последовательно прочитать всю таблицу)
bool ReadRecord(wxString field,uint4 val); //найти и прочитать запись
bool ReadRecord(TiptopStream* is); //Позиция на нужном месте читаем запись из потока
bool ReadRecord(int4 pos); //Перемещяем курсор в заданную позицию и читаем запись
bool ReadNextRecord(bool savePos = false); //Прочитать следующую запись из таблицы
bool ReadRecordByNum(uint4 num); //Читать запись по её порядковому номеру (должны заранее знать позиции записей)
bool WriteHeader(); ///<Записать заголовок таблицы в поток m_os (Используется при создании таблицы)
bool WriteRecord(bool savePos , bool findPos); ///<Записать 1 запись в конец m_os из полей (TODO как сделаю findFreeSize то в пустую позицию)
bool WriteRecord(); //Записать запись в текущее положение потока m_os
//bool WriteTableToStream(wxOutputStream* os); ///<Записать заголовок и тело таблицы в предоставленный поток
void addRecPos(uint8 pos) //Добавить позицию записи в памяти
{ m_Pos.add(pos);
};
bool delRecPos(uint8 pos); //Удалить позицию записи в памяти
//uint4 getNumByRecPos(uint8 pos)//Получить порядковый номер записи по позиции
/*uint4 GetRecPos(uint4 num) //Получить адрес записи по позиции
{ if(m_Pos.count()==0) rerturn 0; //TODO исправить добавить bool к параметрам либо возвращять -1
return m_Pos.get(num);
};*/
bool AppendTable(TiptopTable* tb, bool spw, bool spr); //Добавить записи из заданной в текущую (поля должны совпадать по имени и по типу)
bool UpdateTable(TiptopTable* tb); //Обновить записи из заданной в текущую (поля должны совпадать по имени и по типу)
bool UpdateRecord(); ///<Обновить 1 запись удаляем потом пишем
};
//---------------------------------------------------------------------------
//база данных (набор таблиц)
class TiptopBD
{
private:
wxFileOutputStream* m_fos; //поток чтения из файла
wxFileInputStream* m_fis; //поток записи в файл
TSimpleList<TiptopTable*>* list;//список таблиц
uint4 m_maxid; //макс id таблиц (нумерация с 1)
bool m_ok; //Нормально ли открылась база
public:
wxString m_path; //путь к папке с файлами базы
TiptopBD();
~TiptopBD();
bool Open(wxString path); //задаётся путь к папке где лежит bd.bd файл
bool TableExist(wxString name); //существует ли таблица
//bool AddTable(wxString file); //добавить существующую таблицу в базу данных
TiptopTable* getTable(wxString name); //Получить объект таблицы по её имени
bool ExecSQL(wxString SQL); //выполнить SQL запрос без результата
bool addTable(uint4 id); //Добавить таблицу в базу под заданным id
};
//---------------------------------------------------------------------------
#endif