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 архивы через скрипт.
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

norian: (Default)
Norian

January 2026

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

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 21st, 2026 09:52 pm
Powered by Dreamwidth Studios