первый

This commit is contained in:
2024-11-01 12:23:13 +05:00
parent 801d9d33fa
commit 0688c46a7e
226 changed files with 162921 additions and 0 deletions

490
lib/tiptopbd.h Normal file
View File

@ -0,0 +1,490 @@
//---------------------------------------------------------------------------
#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 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