norian: (Default)
[personal profile] norian
Гибридная Графовая База Данных (отвертка с моторчиком для моделирования моделей)

День 2. Вспомогательная функциональность, системы сборки и тестирования.

Хедер алиасов основных типов (с++). Чтобы не было разных типов в разных местах кода.

source egCoreTypes.h:

#pragma once
#include <iostream>
#include <cstdint>
#include <map>
#include <unordered_map>

typedef unsigned char ByteType;
    // ID types
typedef uint32_t            EgDataNodeIDType;
typedef EgDataNodeIDType    EgDataLinkIDType;
typedef uint16_t            EgBlueprintIDType;     // data nodes and links blueprint/layout/type/class ID type
typedef uint16_t            EgLayerNumType;
    // count/size/length types
typedef uint16_t            EgFieldNameLengthType;
typedef uint8_t             EgFieldsCountType;
typedef uint16_t            EgStrSizeType;
typedef uint64_t            StaticLengthType;
    // map types
class EgDataNode;
class EgDataLinkType;
typedef std::unordered_map <EgDataNodeIDType, EgDataNode*>  EgDataNodesMapType;
typedef std::map <EgDataNodeIDType, EgDataNode*>            EgDataNodesOrdMapType;
typedef std::unordered_map <EgDataLinkIDType, EgDataNode*>  EgLinkIDsNodePtrsMapType;
typedef std::unordered_map <EgDataNode*, EgDataNode*>   EgLinkDataPtrsNodePtrsMapType;

const uint64_t egDefaultHamBrickSize = 1024;


Система аллокации памяти для небольших кусочков данных "Ham Slicer". При использовании системной аллокации для таких данных получается значительный оверхед и повышенные риски утечек. Ham Slicer аллоцирует относительно большие блоки памяти и выделяет кусочки из них под поля данных.

source egHamSlicer.h:
<
#pragma once
#include "../metainfo/egCoreTypes.h"

typedef uint32_t EgHamBrickIDType;

struct EgHamBrickType {
    EgHamBrickIDType brickID;
    ByteType* brickPtr;
    uint64_t freeSize;
    uint64_t usedSlicesCount;
};

class EgHamSlicerType {
public:
    EgHamBrickIDType    nextID      {1};        // ham bricks counter
    EgHamBrickType*     newBrickPtr {nullptr};
    EgHamBrickType      newHamBrick;
    uint64_t            hamBrickSize {egDefaultHamBrickSize};
    // uint64_t  dataSize      {0};

    std::unordered_map <EgHamBrickIDType, EgHamBrickType>  hamBricks;   // all ham bricks
    std::multimap <uint64_t, EgHamBrickType*>    hamBricksByFree;       // free slices of bricks lookup

    EgHamSlicerType () { initBrick(0); }
    ~EgHamSlicerType() { hamBricksByFree.clear(); hamBricks.clear(); }

    bool initBrick(uint64_t sliceSize) { // get new ham brick
        newBrickPtr = nullptr;
        if (sliceSize > hamBrickSize)
            return false;
        newHamBrick.brickPtr = new ByteType[hamBrickSize];
        newHamBrick.brickID  = nextID++;
        newHamBrick.freeSize = hamBrickSize - sliceSize;
        newHamBrick.usedSlicesCount = sliceSize > 0 ? 1 : 0;
        if (newHamBrick.brickPtr) {
            auto bricsIter = hamBricks.insert(std::make_pair(newHamBrick.brickID, newHamBrick)); // copy to map
            newBrickPtr = &(bricsIter.first-> second);
            hamBricksByFree.insert(std::make_pair(newHamBrick.freeSize, newBrickPtr));
        }
        // std::cout << "EgHamSlicerType initBrick() exit" << std::endl;
        return (newHamBrick.brickPtr);
    }
    bool getSlice(uint64_t sliceSize, EgHamBrickIDType& brickID, ByteType*& slicePtr) {
        bool found {false};
            // search by size
        for (auto [first, second] : hamBricksByFree) // 11 auto bricsIter :, <11 = dataFieldsNames.begin(); fieldsIter != dataFieldsNames.end(); ++fieldsIter) {
            if (first >= sliceSize) { // get existing ham brick
                brickID = second-> brickID;
                slicePtr = second-> brickPtr + hamBrickSize - second->freeSize;
                second-> freeSize -= sliceSize;
                second-> usedSlicesCount++;
                auto nodeHandler = hamBricksByFree.extract(first); // update key of map with magic 17 code
                nodeHandler.key() = second->freeSize;
                hamBricksByFree.insert(std::move(nodeHandler));
                found = true;
                break;
            }
        if (!found) { // get new ham brick
            if (initBrick(sliceSize)) {
                brickID  = newHamBrick.brickID;
                slicePtr = newHamBrick.brickPtr;
            } else
                return false;
        }
        // std::cout << "EgHamSlicerType getSlice() ok " << brickID << " " <<  sliceSize << std::endl;
        return true; 
    }
    void freeSlice(EgHamBrickIDType brickID) {  // check if ham brick totally consumed TODO recycle brick
        auto iter = hamBricks.find(brickID); // search hamBricks by ID
        if (iter != hamBricks.end())
            iter-> second.usedSlicesCount--;
        if (!iter-> second.usedSlicesCount) { // all slices released, search hamBricksByFree by its freeSize range to remove
            for (auto [sizeIter, rangeEnd] = hamBricksByFree.equal_range(iter->second.freeSize); sizeIter != rangeEnd; ++sizeIter)
                if (sizeIter->second-> brickID == brickID) {
                    hamBricksByFree.erase(sizeIter);
                    break;
                }
            delete iter-> second.brickPtr;
            hamBricks.erase(iter);
        }
    }
};

void PrintHamSlices(EgHamSlicerType theSlicer);


Все данные в гибридной графовой базе хранятся в универсальном типе egByteArray. Контроль и интерпретация типов делается логикой приложения.

source egByteArray.h:

#pragma once
#include <cstring>
#include "egHamSlicer.h"

class EgByteArrayAbstractType {
public:
    uint64_t  dataSize          {0};
    // uint64_t  dataChunkCapacity {0};
    ByteType* dataChunk         {nullptr};

    EgByteArrayAbstractType () {}
    EgByteArrayAbstractType (uint64_t init_size) 
        : dataSize(init_size)
        // , dataChunkCapacity(init_size)
        {}
    EgByteArrayAbstractType (ByteType* init_data, uint64_t init_size)
        : dataSize(init_size)
        // , dataChunkCapacity(init_size)
        , dataChunk(init_data) {}
    virtual ~EgByteArrayAbstractType() {}

    virtual void reallocDataChunk(uint64_t newSize) { std::cout << "ERROR: reallocDataChunk() of abstract class called" << std::endl; }

    EgByteArrayAbstractType& operator = (const EgByteArrayAbstractType& rightBA);

    template<typename T> EgByteArrayAbstractType& operator >> (T& value) {
        value = *(reinterpret_cast<T*> (this-> dataChunk));
        return *this; }

    template<typename T> EgByteArrayAbstractType& operator << (const T& value) {
        reallocDataChunk(sizeof(T));    
        *(reinterpret_cast<T*> (dataChunk)) = value;
        return *this; }        
};

class EgByteArraySlicerType : public EgByteArrayAbstractType { // egBA with ham slicer mem allocator
public:
    EgHamSlicerType* theHamSlicer {nullptr};
    EgHamBrickIDType brickID      {0};

    EgByteArraySlicerType () = delete;
    EgByteArraySlicerType (EgHamSlicerType* a_HamSlicer, uint64_t init_size = 0): EgByteArrayAbstractType(init_size)
        , theHamSlicer(a_HamSlicer)
    { if(init_size) theHamSlicer-> getSlice(dataSize, brickID, dataChunk); }

    EgByteArraySlicerType (EgHamSlicerType& a_HamSlicer, uint64_t init_size = 0): EgByteArrayAbstractType(init_size)
        , theHamSlicer(&a_HamSlicer)
    { if(init_size) theHamSlicer-> getSlice(dataSize, brickID, dataChunk); }

    EgByteArraySlicerType (EgByteArraySlicerType& copySliceBA) // copy constructor
    { dataSize = copySliceBA.dataSize; if(dataSize) { theHamSlicer-> getSlice(dataSize, brickID, dataChunk); 
      memcpy((void*)dataChunk, (void*) copySliceBA.dataChunk, dataSize);} }

    virtual ~EgByteArraySlicerType() {
        if (theHamSlicer && brickID) theHamSlicer-> freeSlice(brickID);
    }

    void reallocDataChunk(uint64_t newSize) override;
};

class EgByteArraySysallocType : public EgByteArrayAbstractType { // egBA with system mem allocator
public:
    EgByteArraySysallocType () {} 
    EgByteArraySysallocType (uint64_t init_size): EgByteArrayAbstractType(init_size)
        { if(init_size) dataChunk = new ByteType[init_size]; }
    virtual ~EgByteArraySysallocType() { /*std::cout << "destr. of "; PrintByteArray(*this);*/
        if(dataSize) delete dataChunk;
    }

    void reallocDataChunk(uint64_t newSize) override;
};

void ByteArrayFromCharStr(const char* str, EgByteArrayAbstractType& byteArray);

template <typename T> void ByteArrayFromType(T&& value, EgByteArrayAbstractType& byteArray) {
    // std::cout << "ByteArrayFromType() value: " << value << std::endl;
    byteArray.reallocDataChunk(sizeof(value));
    memcpy((void*)byteArray.dataChunk, (void*) &value, byteArray.dataSize);
}

EgByteArrayAbstractType& operator >> (EgByteArrayAbstractType& byteArray, char* str);
EgByteArrayAbstractType& operator << (EgByteArrayAbstractType& byteArray, const char* str);

EgByteArrayAbstractType& operator >> (EgByteArrayAbstractType& byteArray, std::string& str);
EgByteArrayAbstractType& operator << (EgByteArrayAbstractType& byteArray, const std::string& str);

// ======================== Debug ========================
void PrintByteArray(EgByteArrayAbstractType& bArray, bool isStr = true);


Чтобы хранить egByteArray с данными узла, создан класс EgPtrArrayType, который просто хранит несколько ссылок без контейнерного оверхеда.
Для унификации файловых и потоковых операций имплементированы соответствующие API - EgFileType и EgDataStream.

Система сборки основана на make как наиболее простой и обкатанной утилите. Сделаны отдельные конфигурации для сборки библиотеки и тестовых приложений плюс скрипты для сборки и запуска.

source Makefile.lib

all: makethelib clean
.PHONY: all clean

# CFLAGS = -O3 -Wall -Wall -Wextra -pedantic -fPIC

TARGET = libegdb.so

CC = g++
LDLIBS += -lstdc++fs
CPPFLAGS = -fPIC -std=c++20
INCLUDES =
# -I/home/fran/some_directory

# SRC_FILES = tests/testDataFields.cpp service/egByteArray.cpp nodes/egDataNode.cpp
SRC_FILES_SERVICE = $(shell find service -name '*.cpp')
SRC_FILES_NODES = $(shell find nodes -name '*.cpp')
SRC_FILES_INDEXES = $(shell find indexes -name '*.cpp')
SRC_FILES_LINKS = $(shell find links -name '*.cpp')
SRC_FILES_METAINFO = $(shell find metainfo -name '*.cpp')
# SRC_FILES_QTINTERFACE = $(shell find qtinterface -name '*.cpp')
SRC_FILES = $(SRC_FILES_SERVICE) $(SRC_FILES_NODES) $(SRC_FILES_INDEXES) $(SRC_FILES_LINKS) $(SRC_FILES_METAINFO) 
# $(SRC_FILES_QTINTERFACE)

#   $(name:string1=string2) For each word in 'name' replace 'string1' with 'string2'
OBJS = $(SRC_FILES:.cpp=.o)
    
makethelib:    $(TARGET)
	@echo ===============  Finished compilation of $(TARGET) ===============
	@ls -la | grep $(TARGET)

# compile object file to .so shared library $(INCLUDES)

$(TARGET): $(OBJS)
	$(CC) -shared -o $(TARGET) $(OBJS)

# compile source files to object files
# this is a suffix replacement rule for building .o's from .c's
# it uses automatic variables $<: the name of the prerequisite of
# the rule(a .c file) and $@: the name of the target of the rule (a .o file)

%.o : %.cpp
	$(CC) $(CPPFLAGS) $(INCLUDES) -c $< -o $@

clean:
	@echo ===============  clean  ===============
	$(RM) $(OBJS)


Тестовые приложения запускают необходимую функциональность и пишут в лог строчку PASS или FAIL, скрипт потом их суммирует.

source all_tests.sh:

#!/bin/bash

./clean.sh

# rebuild
echo ============ build lib and test apps ============

make -f Makefile.lib
make -f Makefile.tests

echo ============ run test apps ============

mv tests/*.exe tests_exec/

cd tests_exec
# exec all *.exe to log file
for i in *.exe
do 
  LD_LIBRARY_PATH=~/egdb/src ./$i 2>&1 | tee -a all_tests.log 
done
echo
# mv tests_exec/all_tests.log ./

PASSED_COUNT=`cat all_tests.log | grep PASS | wc -w`	
echo PASS total: $PASSED_COUNT

FAILED_COUNT=`cat all_tests.log | grep FAIL | wc -w`
echo FAIL total: $FAILED_COUNT

cd ..


Резервное копирование производится двумя способами - git с внешним репозиторием GitHub плюс локальные tgz архивы через скрипт.

заметка на полях

Date: 2026-01-20 02:28 pm (UTC)
From: [personal profile] dedekha
Переименовывание целых типов любимая игра очень многих, однако не решает ни одной проблемы из-за aвтоматической конверсии, использование всех и всяческих true-типов ломает templates.

Хороших вариантов нет.

Profile

norian: (Default)
Norian

January 2026

S M T W T F S
    1 2 3
456 78 910
11121314 151617
18 19 20 21 222324
25262728293031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 22nd, 2026 08:24 pm
Powered by Dreamwidth Studios