Adding objects GUI initial. Changing TODO. Some fixes along side
This commit is contained in:
parent
f9d62c324d
commit
112fea2f04
18 changed files with 1345 additions and 56 deletions
|
|
@ -164,7 +164,7 @@ bool HeapAllocGlobal::checkLeaks() {
|
|||
}
|
||||
|
||||
// 1) Check for not deallocated memory
|
||||
if (mNumAllocations && ignoredCount != mNumAllocations) {
|
||||
if (mNumAllocations && ignoredCount < mNumAllocations) {
|
||||
|
||||
#ifdef MEM_STACK_TRACE
|
||||
for (auto iter = mEntry; iter; iter = iter->mPrev) {
|
||||
|
|
@ -172,7 +172,7 @@ bool HeapAllocGlobal::checkLeaks() {
|
|||
}
|
||||
#endif
|
||||
|
||||
printf(" Count : %llu", mNumAllocations);
|
||||
printf(" Count : %llu", mNumAllocations - ignoredCount);
|
||||
|
||||
ASSERT(!"Destruction of not freed Allocator")
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -177,8 +177,9 @@ namespace tp {
|
|||
|
||||
void popBack() {
|
||||
DEBUG_ASSERT(mLast)
|
||||
auto const tmp = mLast;
|
||||
detach(mLast);
|
||||
deleteNode(mLast);
|
||||
deleteNode(tmp);
|
||||
}
|
||||
|
||||
void popFront() {
|
||||
|
|
|
|||
2
Externals/imgui
vendored
2
Externals/imgui
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 1109de38277fd2d14d4dca4c1cb8d4a2c4ff0f95
|
||||
Subproject commit 4144d7d772a800f08693b8b13f7c81f1f22a73c4
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
#include "Window.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
int main() {
|
||||
tp::ModuleManifest* deps[] = { &tp::gModuleGraphics, nullptr };
|
||||
tp::ModuleManifest testModule("Example", nullptr, nullptr, deps);
|
||||
|
|
@ -10,12 +12,16 @@ int main() {
|
|||
}
|
||||
|
||||
{
|
||||
tp::HeapAllocGlobal::startIgnore();
|
||||
auto window = tp::Window::createWindow(800, 600, "Window 1");
|
||||
tp::HeapAllocGlobal::stopIgnore();
|
||||
|
||||
if (window) {
|
||||
window->renderLoop();
|
||||
while (!window->shouldClose()) {
|
||||
window->processEvents();
|
||||
|
||||
ImGui::Text("Hello!");
|
||||
|
||||
window->draw();
|
||||
}
|
||||
}
|
||||
|
||||
tp::Window::destroyWindow(window);
|
||||
|
|
|
|||
|
|
@ -42,10 +42,14 @@ void Graphics::GUI::deinit() {
|
|||
}
|
||||
|
||||
void Graphics::GUI::proc() {
|
||||
tp::HeapAllocGlobal::startIgnore();
|
||||
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
tp::HeapAllocGlobal::stopIgnore();
|
||||
|
||||
// ImGui code goes here
|
||||
ImGui::Begin("Window");
|
||||
ImGui::End();
|
||||
|
|
|
|||
|
|
@ -71,11 +71,13 @@ void Graphics::deinit() {
|
|||
mGl.deinit();
|
||||
}
|
||||
|
||||
void Graphics::draw() {
|
||||
void Graphics::proc() {
|
||||
mGl.proc();
|
||||
mCanvas.proc();
|
||||
mGui.proc();
|
||||
}
|
||||
|
||||
void Graphics::draw() {
|
||||
mGl.draw();
|
||||
mCanvas.draw();
|
||||
mGui.draw();
|
||||
|
|
@ -110,6 +112,8 @@ Window::~Window() {
|
|||
|
||||
|
||||
Window* Window::createWindow(int width, int height, const char* title) {
|
||||
tp::HeapAllocGlobal::startIgnore();
|
||||
|
||||
static int count = 1;
|
||||
if (!count) {
|
||||
printf("Window class is a singleton\n");
|
||||
|
|
@ -128,7 +132,10 @@ Window* Window::createWindow(int width, int height, const char* title) {
|
|||
printf("GLFW Error: %i %s\n", error, description);
|
||||
});
|
||||
|
||||
return new Window(width, height, title);
|
||||
auto out = new Window(width, height, title);
|
||||
|
||||
tp::HeapAllocGlobal::stopIgnore();
|
||||
return out;
|
||||
}
|
||||
|
||||
void Window::destroyWindow(Window* window) {
|
||||
|
|
@ -136,15 +143,18 @@ void Window::destroyWindow(Window* window) {
|
|||
glfwTerminate();
|
||||
}
|
||||
|
||||
void Window::renderLoop() {
|
||||
while (!glfwWindowShouldClose(mContext->window)) {
|
||||
bool Window::shouldClose() const {
|
||||
return glfwWindowShouldClose(mContext->window);
|
||||
}
|
||||
|
||||
mGraphics.draw();
|
||||
|
||||
// Swap buffers and poll events
|
||||
glfwSwapBuffers(mContext->window);
|
||||
void Window::processEvents() {
|
||||
glfwPollEvents();
|
||||
}
|
||||
mGraphics.proc();
|
||||
}
|
||||
|
||||
void Window::draw() {
|
||||
mGraphics.draw();
|
||||
glfwSwapBuffers(mContext->window);
|
||||
}
|
||||
|
||||
auto Window::getContext() -> Context* { return mContext; }
|
||||
|
|
@ -67,6 +67,7 @@ namespace tp {
|
|||
void init(Window* window);
|
||||
void deinit();
|
||||
void draw();
|
||||
void proc();
|
||||
|
||||
private:
|
||||
GUI mGui;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,10 @@ namespace tp {
|
|||
static void destroyWindow(Window* window);
|
||||
|
||||
public:
|
||||
void renderLoop();
|
||||
void draw();
|
||||
void processEvents();
|
||||
[[nodiscard]] bool shouldClose() const;
|
||||
|
||||
auto getContext() -> Context*;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ namespace tp {
|
|||
|
||||
// argument isInside
|
||||
bool isInside(const Vec2<Type>& p) const {
|
||||
return (p.each_compre(pos) && (pos + size).each_compre(p));
|
||||
return isInside(p.x, p.y);
|
||||
}
|
||||
|
||||
bool isInside(Type x, Type y) const {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ project(Objects)
|
|||
|
||||
### ---------------------- Static Library --------------------- ###
|
||||
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
|
||||
file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp")
|
||||
file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp" "./applications/*.hpp")
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
||||
|
||||
|
|
@ -17,9 +17,11 @@ target_link_libraries(${PROJECT_NAME} PUBLIC Strings Math Tokenizer CommandLine
|
|||
### -------------------------- Applications -------------------------- ###
|
||||
#add_executable(osc ./applications/Compiler.cpp)
|
||||
#add_executable(osi ./applications/Interpreter.cpp)
|
||||
add_executable(osg ./applications/GUI.cpp ./applications/GUIEntry.cpp)
|
||||
|
||||
#target_link_libraries(osc ${PROJECT_NAME})
|
||||
#target_link_libraries(osi ${PROJECT_NAME})
|
||||
target_link_libraries(osg ${PROJECT_NAME} Graphics Imgui)
|
||||
|
||||
### -------------------------- Tests -------------------------- ###
|
||||
enable_testing()
|
||||
|
|
|
|||
1109
Objects/applications/GUI.cpp
Normal file
1109
Objects/applications/GUI.cpp
Normal file
File diff suppressed because it is too large
Load diff
73
Objects/applications/GUI.h
Normal file
73
Objects/applications/GUI.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "primitives/primitives.h"
|
||||
#include "primitives//interpreterobject.h"
|
||||
|
||||
#include "compiler/function.h"
|
||||
|
||||
#include "strings.h"
|
||||
|
||||
namespace obj {
|
||||
|
||||
class ObjectsGUI {
|
||||
|
||||
enum { MAX_NAME_LENGHT = 102 };
|
||||
char mNameEdit [MAX_NAME_LENGHT];
|
||||
obj::Object* mLastNameEditObject = NULL;
|
||||
|
||||
struct ViewStackNode {
|
||||
obj::Object* obj;
|
||||
tp::String id;
|
||||
ViewStackNode() { obj = NULL; }
|
||||
ViewStackNode(obj::Object* obj, tp::String id) : obj(obj), id(id) {}
|
||||
operator bool() { return obj; }
|
||||
};
|
||||
|
||||
tp::List<ViewStackNode> mViewStack;
|
||||
obj::Object* mRoot = NULL;
|
||||
obj::Object* mActive = NULL;
|
||||
obj::Object* mClipboard = NULL;
|
||||
|
||||
void setClipboard(obj::Object*);
|
||||
obj::Object* getClipboard();
|
||||
|
||||
void* mImGuiCtx = NULL;
|
||||
|
||||
public:
|
||||
|
||||
bool mShowDebugInfo = true;
|
||||
bool mCaptureInput = false;
|
||||
|
||||
ObjectsGUI();
|
||||
~ObjectsGUI();
|
||||
|
||||
void cd(obj::Object* child, tp::String name);
|
||||
void cdup();
|
||||
|
||||
void clearEvents();
|
||||
|
||||
void draw();
|
||||
void explorer();
|
||||
void properties(const obj::ObjectType*, bool top_of_tree_vew = true);
|
||||
void drawFps();
|
||||
private:
|
||||
|
||||
void preview(obj::Object* obj);
|
||||
ViewStackNode enumView(obj::EnumObject* obj);
|
||||
ViewStackNode boolView(obj::BoolObject* obj);
|
||||
ViewStackNode floatView(obj::FloatObject* obj);
|
||||
ViewStackNode nullView(obj::NullObject* obj);
|
||||
ViewStackNode linkoView(obj::LinkObject* obj);
|
||||
ViewStackNode intoView(obj::IntObject* obj);
|
||||
ViewStackNode dictView(obj::DictObject* obj);
|
||||
ViewStackNode listView(obj::ListObject* obj);
|
||||
ViewStackNode stringView(obj::StringObject* in);
|
||||
ViewStackNode classView(obj::ClassObject* in);
|
||||
ViewStackNode methodView(obj::MethodObject* in);
|
||||
ViewStackNode colorView(obj::ColorObject* in);
|
||||
ViewStackNode interpreterView(obj::InterpreterObject* in);
|
||||
void dictViewEdit(obj::DictObject* dict, tp::String item_id, obj::Object* obj, bool& popup);
|
||||
void dictViewDrawCreate(obj::DictObject* dict);
|
||||
};
|
||||
};
|
||||
50
Objects/applications/GUIEntry.cpp
Normal file
50
Objects/applications/GUIEntry.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
#include "NewPlacement.hpp"
|
||||
#include "Window.hpp"
|
||||
|
||||
#include "GUI.h"
|
||||
|
||||
using namespace tp;
|
||||
using namespace obj;
|
||||
|
||||
class GUIWindow {
|
||||
Window* window;
|
||||
ObjectsGUI gui;
|
||||
|
||||
public:
|
||||
GUIWindow() {
|
||||
tp::HeapAllocGlobal::startIgnore();
|
||||
window = Window::createWindow(1500, 900, "Objects GUI");
|
||||
tp::HeapAllocGlobal::stopIgnore();
|
||||
|
||||
gui.cd(NDO->create("dict"), "root");
|
||||
}
|
||||
|
||||
void run() {
|
||||
while (!window->shouldClose()) {
|
||||
window->processEvents();
|
||||
gui.draw();
|
||||
window->draw();
|
||||
}
|
||||
}
|
||||
|
||||
~GUIWindow() {
|
||||
Window::destroyWindow(window);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
||||
tp::ModuleManifest* deps[] = { &gModuleObjects, &gModuleGraphics, nullptr };
|
||||
tp::ModuleManifest module("ObjectsTests", nullptr, nullptr, deps);
|
||||
|
||||
if (module.initialize()) {
|
||||
{
|
||||
GUIWindow window;
|
||||
window.run();
|
||||
}
|
||||
module.deinitialize();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -26,42 +26,42 @@ namespace tp {
|
|||
return iter - in;
|
||||
}
|
||||
|
||||
static tChar* insertLogic(const tChar* cur, const tChar* tar, Index at, Index len) {
|
||||
static void insertLogic(tChar* result, Index resultLen, const tChar* cur, const tChar* tar, Index at, Index len) {
|
||||
auto curLen = calcLength(cur);
|
||||
auto allLen = curLen + len;
|
||||
auto* out = new tChar[allLen + 1];
|
||||
ASSERT(allLen <= resultLen);
|
||||
|
||||
DEBUG_ASSERT(curLen >= 0)
|
||||
DEBUG_ASSERT(len >= 0)
|
||||
DEBUG_ASSERT(at < curLen + 1 && at >= 0)
|
||||
|
||||
for (Index idx = 0; idx < at; idx++) {
|
||||
out[idx] = cur[idx];
|
||||
result[idx] = cur[idx];
|
||||
}
|
||||
for (Index idx = 0; idx < len; idx++) {
|
||||
out[idx + at] = tar[idx];
|
||||
result[idx + at] = tar[idx];
|
||||
}
|
||||
for (Index idx = at + len; idx < allLen; idx++) {
|
||||
out[idx] = cur[idx - len];
|
||||
result[idx] = cur[idx - len];
|
||||
}
|
||||
out[allLen] = '\0';
|
||||
return out;
|
||||
result[allLen] = '\0';
|
||||
}
|
||||
|
||||
// including end not including start
|
||||
static tChar* removeLogic(const tChar* cur, Index start, Index end) {
|
||||
static void removeLogic(tChar* result, Index resultLen, const tChar* cur, Index start, Index end) {
|
||||
auto curLen = calcLength(cur);
|
||||
auto rangeLen = end - start;
|
||||
auto newLen = curLen - rangeLen;
|
||||
auto* out = new tChar[newLen + 1];
|
||||
out[newLen] = '\0';
|
||||
|
||||
ASSERT(newLen <= resultLen);
|
||||
|
||||
result[newLen] = '\0';
|
||||
for (Index idx = 0; idx < start; idx++) {
|
||||
out[idx] = cur[idx];
|
||||
result[idx] = cur[idx];
|
||||
}
|
||||
for (Index idx = end; idx < curLen; idx++) {
|
||||
out[idx - rangeLen] = cur[idx];
|
||||
result[idx - rangeLen] = cur[idx];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool isEqualLogic(const tChar* left, const tChar* right) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace tp {
|
|||
class StringData {
|
||||
friend StringTemplate<tChar>;
|
||||
typedef StringLogic<tChar> Logic;
|
||||
typedef typename StringLogic<tChar>::Index Index;
|
||||
|
||||
private:
|
||||
uint2 mIsConst; // source is non-modifiable
|
||||
|
|
@ -38,7 +39,7 @@ namespace tp {
|
|||
mEditedIdx = 0;
|
||||
}
|
||||
|
||||
explicit StringData(const tChar* aBuff) {
|
||||
explicit StringData(tChar* aBuff) {
|
||||
MODULE_SANITY_CHECK(gModuleStrings)
|
||||
DEBUG_ASSERT(aBuff)
|
||||
auto const len = Logic::calcLength(aBuff);
|
||||
|
|
@ -58,7 +59,7 @@ namespace tp {
|
|||
if (!mIsConst) delete[] mBuff;
|
||||
}
|
||||
|
||||
void resize(ualni size) {
|
||||
void resize(Index size) {
|
||||
DEBUG_ASSERT(mReferenceCount == 1 && !mIsConst)
|
||||
delete[] mBuff;
|
||||
mBuff = new tChar[size + 1];
|
||||
|
|
@ -143,7 +144,7 @@ namespace tp {
|
|||
}
|
||||
|
||||
// output will be null if in TextEditing mode
|
||||
tChar* resize(uint1 size) {
|
||||
tChar* resize(Index size) {
|
||||
makeModifiable();
|
||||
mData->resize(size);
|
||||
return write();
|
||||
|
|
@ -231,6 +232,12 @@ namespace tp {
|
|||
Logic::calcLineOffsets(read(), size(), aOut);
|
||||
}
|
||||
|
||||
StringTemplate& operator=(tChar* in) {
|
||||
this->~StringTemplate();
|
||||
new (this) StringTemplate(in);
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringTemplate& operator=(const tChar* in) {
|
||||
this->~StringTemplate();
|
||||
new (this) StringTemplate(in);
|
||||
|
|
@ -250,6 +257,14 @@ namespace tp {
|
|||
return Logic::isEqualLogic(in.read(), read());
|
||||
}
|
||||
|
||||
StringTemplate operator+(const StringTemplate& in) const {
|
||||
StringTemplate out;
|
||||
auto const newSize = in.size() + size();
|
||||
out.resize(newSize);
|
||||
Logic::insertLogic(out.write(), newSize, read(), in.read(), size(), in.size());
|
||||
return out;
|
||||
}
|
||||
|
||||
public: // Debugging
|
||||
[[nodiscard]] ualni getReferenceCount() const { return mData->mReferenceCount; }
|
||||
[[nodiscard]] bool getIsConstFlag() const { return mData->mIsConst; }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,18 @@ struct TestStruct {
|
|||
String str = "test data";
|
||||
};
|
||||
|
||||
TEST_DEF_STATIC(Addition) {
|
||||
String str1 = "abc";
|
||||
String str2 = "def";
|
||||
{
|
||||
str1 + str2;
|
||||
}
|
||||
{
|
||||
TEST(str1 + str2 == "abcdef");
|
||||
TEST(str1 + "aaaccc" == "abcaaaccc");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_DEF_STATIC(Simple) {
|
||||
String str;
|
||||
TEST(str.getIsConstFlag());
|
||||
|
|
@ -35,4 +47,5 @@ TEST_DEF_STATIC(Simple) {
|
|||
|
||||
TEST_DEF(Strings) {
|
||||
testSimple();
|
||||
testAddition();
|
||||
}
|
||||
|
|
@ -33,78 +33,78 @@ TEST_DEF(calcLength) {
|
|||
|
||||
TEST_DEF(insertLogic_emptyTarget) {
|
||||
// Test insertLogic function with an empty target string
|
||||
char inserted[40] = { "" };
|
||||
const char* cur = "Hello";
|
||||
const char* tar = "";
|
||||
const char* result = "Hello";
|
||||
char* inserted = Logic::insertLogic(cur, tar, 5, 0);
|
||||
Logic::insertLogic(inserted, 40, cur, tar, 5, 0);
|
||||
TEST(Logic::isEqualLogic(inserted, result));
|
||||
delete[] inserted;
|
||||
}
|
||||
|
||||
TEST_DEF(insertLogic_emptyCurrent) {
|
||||
// Test insertLogic function with an empty current string
|
||||
char inserted[40] = { "" };
|
||||
const char* cur = "";
|
||||
const char* tar = "World";
|
||||
const char* result = "World";
|
||||
char* inserted = Logic::insertLogic(cur, tar, 0, 5);
|
||||
Logic::insertLogic(inserted, 40, cur, tar, 0, 5);
|
||||
TEST(Logic::isEqualLogic(inserted, result));
|
||||
delete[] inserted;
|
||||
}
|
||||
|
||||
TEST_DEF(insertLogic_curLengthLessThanPos) {
|
||||
// Test insertLogic function with current length less than the insert position
|
||||
char inserted[40] = { "" };
|
||||
const char* cur = "Hello";
|
||||
const char* tar = "World";
|
||||
const char* result = "HelloWorld";
|
||||
char* inserted = Logic::insertLogic(cur, tar, 10, 5);
|
||||
Logic::insertLogic(inserted, 40, cur, tar, 10, 5);
|
||||
TEST(Logic::isEqualLogic(inserted, result));
|
||||
delete[] inserted;
|
||||
}
|
||||
|
||||
TEST_DEF(removeLogic_emptyCurrent) {
|
||||
// Test removeLogic function with an empty current string
|
||||
char removed[40] = { "" };
|
||||
const char* cur = "";
|
||||
const char* result = "";
|
||||
char* removed = Logic::removeLogic(cur, 0, 0);
|
||||
Logic::removeLogic(removed, 40, cur, 0, 0);
|
||||
TEST(Logic::isEqualLogic(removed, result));
|
||||
delete[] removed;
|
||||
}
|
||||
|
||||
TEST_DEF(removeLogic_removeUntilEnd) {
|
||||
// Test removeLogic function by removing until the end of the current string
|
||||
char removed[40] = { "" };
|
||||
const char* cur = "HelloWorld";
|
||||
const char* result = "World";
|
||||
char* removed = Logic::removeLogic(cur, 0, 5);
|
||||
Logic::removeLogic(removed, 40, cur, 0, 5);
|
||||
TEST(Logic::isEqualLogic(removed, result));
|
||||
delete[] removed;
|
||||
}
|
||||
|
||||
TEST_DEF(removeLogic_removeMiddleCharacters) {
|
||||
// Test removeLogic function by removing characters in the middle of the current string
|
||||
char removed[40] = { "" };
|
||||
const char* cur = "HelloWorld";
|
||||
const char* result = "Helrld";
|
||||
char* removed = Logic::removeLogic(cur, 3, 7);
|
||||
Logic::removeLogic(removed, 40, cur, 3, 7);
|
||||
TEST(Logic::isEqualLogic(removed, result));
|
||||
delete[] removed;
|
||||
}
|
||||
|
||||
TEST_DEF(insertLogicGood) {
|
||||
// Test insertLogic function
|
||||
char inserted[40] = { "" };
|
||||
const char* cur = "Hello";
|
||||
const char* tar = "World";
|
||||
const char* result = "HelloWorld";
|
||||
char* inserted = Logic::insertLogic(cur, tar, 5, 5);
|
||||
Logic::insertLogic(inserted, 40, cur, tar, 5, 5);
|
||||
TEST(Logic::isEqualLogic(inserted, result));
|
||||
delete[] inserted;
|
||||
}
|
||||
|
||||
TEST_DEF(removeLogicGood) {
|
||||
// Test removeLogic function
|
||||
char removed[40] = { "" };
|
||||
const char* cur = "HelloWorld";
|
||||
const char* result = "Helo";
|
||||
char* removed = Logic::removeLogic(cur, 3, 8);
|
||||
Logic::removeLogic(removed, 40, cur, 3, 8);
|
||||
TEST(Logic::isEqualLogic(removed, result));
|
||||
delete[] removed;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
12
TODO
12
TODO
|
|
@ -1,6 +1,13 @@
|
|||
Testing !!
|
||||
|
||||
Graphics:
|
||||
Window event system
|
||||
Graphics API
|
||||
FPS count & Performance control
|
||||
|
||||
Objects:
|
||||
Debug GUI
|
||||
use anonymous namespaces for object static methods
|
||||
write tests before refactor !!
|
||||
Alloc Size queries
|
||||
remove obj_ref_count macro
|
||||
|
|
@ -10,11 +17,6 @@ Objects:
|
|||
remove tp::
|
||||
rename classes
|
||||
|
||||
Graphics:
|
||||
Window event system
|
||||
Graphics API
|
||||
FPS count & Performance control
|
||||
|
||||
Containers:
|
||||
Test generatePermutations
|
||||
Add variant size buffer
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue