├── bench.ods ├── virtmem ├── TODO.txt ├── src │ ├── utils.cpp │ ├── virtmem.h │ ├── internal │ │ ├── utils.h │ │ ├── serial_utils.h │ │ ├── serial_utils.hpp │ │ ├── base_alloc.h │ │ ├── vptr_utils.h │ │ ├── base_vptr.h │ │ ├── alloc.h │ │ ├── vptr.h │ │ └── vptr_utils.hpp │ ├── src.pro │ ├── alloc │ │ ├── static_alloc.h │ │ ├── stdio_alloc.h │ │ ├── sd_alloc.h │ │ ├── spiram_alloc.h │ │ └── serial_alloc.h │ └── config │ │ └── config.h ├── library.properties ├── arduinofy.sh ├── examples │ ├── serial_simple │ │ └── serial_simple.ino │ ├── spiram_simple │ │ └── spiram_simple.ino │ ├── sd_simple │ │ └── sd_simple.ino │ ├── multispiram_simple │ │ └── multispiram_simple.ino │ ├── serial_input │ │ └── serial_input.ino │ ├── alloc_properties │ │ └── alloc_properties.ino │ ├── locking │ │ └── locking.ino │ └── benchmark │ │ ├── benchmark.ino │ │ └── benchmark.h └── extras │ ├── serial_host.py │ └── serialiohandler.py ├── update-docs.sh ├── .gitignore ├── virtmem.pro ├── manual.html ├── benchmark ├── benchmark.pro ├── benchmark.cpp └── deployment.pri ├── gtest └── build-gtest.sh ├── test ├── test.pro ├── test.h ├── test_alloc.cpp ├── test_utils.cpp └── test_wrapper.cpp ├── LICENSE.txt └── README.md /bench.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhelmus/virtmem/HEAD/bench.ods -------------------------------------------------------------------------------- /virtmem/TODO.txt: -------------------------------------------------------------------------------- 1 | docs: 2 | - check if all references are OK 3 | - spell checks 4 | 5 | general: 6 | 7 | -------------------------------------------------------------------------------- /update-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | inkscape -z doc/intro-scheme.svg -e doc/intro-scheme.png -d 65 3 | doxygen doc/Doxyfile 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pro.user 2 | */*.o 3 | *.o 4 | */Makefile 5 | Makefile 6 | src/libvirtmem.a 7 | test/test 8 | doc/html 9 | 10 | -------------------------------------------------------------------------------- /virtmem.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | CONFIG += ordered 3 | SUBDIRS = virtmem/src \ 4 | test \ 5 | benchmark 6 | test.depends = lib 7 | -------------------------------------------------------------------------------- /virtmem/src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "config/config.h" 2 | #include "internal/vptr_utils.h" 3 | 4 | namespace virtmem { 5 | 6 | const NILL_t NILL = NILL_t(); // need empty constructor to prevent compiler error on arduino 7 | 8 | } 9 | -------------------------------------------------------------------------------- /manual.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | The manual can be found in doc/html/index.html. 8 | 9 | 10 | -------------------------------------------------------------------------------- /virtmem/library.properties: -------------------------------------------------------------------------------- 1 | name=virtmem 2 | version=1.0 3 | author=Rick Helmus 4 | maintainer=Rick Helmus 5 | sentence=Virtual memory library to easily extend RAM. 6 | paragraph=This library makes it easy to use external sources such as SPI RAM or a computer to extend the available memory. 7 | category=Data Storage 8 | url=https://github.com/rhelmus/virtmem 9 | architectures=* -------------------------------------------------------------------------------- /virtmem/src/virtmem.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_VIRTMEM_H 2 | #define VIRTMEM_VIRTMEM_H 3 | 4 | #include "config/config.h" 5 | #include "internal/utils.h" 6 | #include "internal/vptr.h" 7 | #include "internal/vptr_utils.h" 8 | 9 | /** 10 | @file 11 | @brief this file must be included in your main sketch (i.e. .ino) file 12 | 13 | @namespace virtmem 14 | @brief contains all code from virtmem 15 | */ 16 | 17 | #endif // VIRTMEM_VIRTMEM_H 18 | -------------------------------------------------------------------------------- /benchmark/benchmark.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | SOURCES += \ 7 | benchmark.cpp 8 | 9 | include(deployment.pri) 10 | qtcAddDeployment() 11 | 12 | INCLUDEPATH += $$PWD/../virtmem/src . 13 | DEPENDPATH += $$PWD/../virtmem/src 14 | 15 | LIBS += -L$$PWD/../virtmem/src/ -lvirtmem 16 | unix:!macx: PRE_TARGETDEPS += $$PWD/../virtmem/src/libvirtmem.a 17 | 18 | QMAKE_CXXFLAGS += -std=gnu++11 19 | -------------------------------------------------------------------------------- /gtest/build-gtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -d gtest ]; then 4 | echo gtest directory already exists! Continuing will wipe it. 5 | read -p "Continue? (y/n)" CONT 6 | if [ $CONT != "y" ]; then 7 | echo "Aborting..." 8 | exit 1 9 | fi 10 | 11 | rm -rf gtest 12 | fi 13 | 14 | git clone https://github.com/google/googletest.git gtest 15 | cd gtest 16 | git checkout tags/release-1.7.0 17 | 18 | mkdir -p build && cd build 19 | cmake -G"Unix Makefiles" .. 20 | make 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/test.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | SOURCES += \ 7 | test_alloc.cpp \ 8 | test_wrapper.cpp \ 9 | test_utils.cpp 10 | 11 | HEADERS += \ 12 | test.h 13 | 14 | INCLUDEPATH += . 15 | 16 | unix:!macx: LIBS += -L$$PWD/../virtmem/src/ -lvirtmem 17 | 18 | INCLUDEPATH += $$PWD/../virtmem/src 19 | DEPENDPATH += $$PWD/../virtmem/src 20 | 21 | unix:!macx: PRE_TARGETDEPS += $$PWD/../virtmem/src/libvirtmem.a 22 | 23 | INCLUDEPATH += ../gtest/gtest/include 24 | LIBS += -L$$PWD/../gtest/gtest/build -lgtest -lgtest_main 25 | DEFINES += __STDC_FORMAT_MACROS 26 | QMAKE_CXXFLAGS_RELEASE += -Os 27 | QMAKE_CXXFLAGS += -std=gnu++11 -pthread 28 | QMAKE_LFLAGS += -pthread 29 | QMAKE += thread 30 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H 2 | #define TEST_H 3 | 4 | #include "gtest/gtest.h" 5 | 6 | #include 7 | 8 | using namespace virtmem; 9 | 10 | typedef StdioVAlloc::TVPtr::type UCharVirtPtr; 11 | typedef StdioVAlloc::TVPtr::type CharVirtPtr; 12 | 13 | class VAllocFixture: public ::testing::Test 14 | { 15 | protected: 16 | StdioVAlloc valloc; 17 | 18 | public: 19 | void SetUp(void) { valloc.setPoolSize(1024 * 1024 * 10); valloc.start(); } 20 | void TearDown(void) { valloc.stop(); } 21 | }; 22 | 23 | template class VPtrFixture: public VAllocFixture 24 | { 25 | protected: 26 | typename StdioVAlloc::TVPtr::type vptr; 27 | 28 | public: 29 | VPtrFixture(void) : vptr() { } // UNDONE: we need this for proper construction, problem? 30 | }; 31 | 32 | #if 0 33 | // From http://stackoverflow.com/a/17236988 34 | inline void print128int(__uint128_t x) 35 | { 36 | printf("__int128: %016" PRIx64 "%016" PRIx64 "\n",(uint64_t)(x>>64),(uint64_t)x); 37 | } 38 | #endif 39 | 40 | #endif // TEST_H 41 | -------------------------------------------------------------------------------- /benchmark/benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | using namespace virtmem; 8 | 9 | enum 10 | { 11 | STDIO_POOLSIZE = 1024 * 128 + 128, 12 | STDIO_BUFSIZE = 1024 * 128, 13 | STDIO_REPEATS = 50 14 | }; 15 | 16 | int main() 17 | { 18 | StdioVAlloc valloc(STDIO_POOLSIZE); 19 | 20 | valloc.start(); 21 | 22 | StdioVAlloc::TVPtr::type buf = valloc.alloc(STDIO_BUFSIZE); 23 | 24 | auto time = std::chrono::high_resolution_clock::now(); 25 | for (int i=0; i(std::chrono::high_resolution_clock::now() - time).count(); 33 | 34 | std::cout << "Finished in " << difftime << " ms\n"; 35 | std::cout << "Speed: " << STDIO_REPEATS * STDIO_BUFSIZE / difftime * 1000 / 1024 << " kB/s\n"; 36 | 37 | return 0; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2016, Rick Helmus 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /virtmem/arduinofy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | COPY="cp -v" 4 | 5 | if [ "$1" = "-s" ]; then 6 | COPY="ln -fvs" shift 7 | fi 8 | 9 | DEST="$1" 10 | echo "destination: $DEST" 11 | 12 | copyExample() 13 | { 14 | mkdir -p "${DEST}/examples/$1" 15 | $COPY "$PWD/../examples/$1/$1.ino" "$DEST/examples/$1" 16 | 17 | if [ -f "${PWD}/../examples/$1/$1.h" ]; then 18 | $COPY "${PWD}/../examples/$1/$1.ino" "${DEST}/examples/$1" 19 | fi 20 | } 21 | 22 | mkdir -p "${DEST}" 23 | $COPY "${PWD}"/alloc.h "${DEST}/" 24 | $COPY "${PWD}"/base_alloc.h "${DEST}/" 25 | $COPY "${PWD}"/base_alloc.cpp "${DEST}/" 26 | $COPY "${PWD}"/base_vptr.h "${DEST}/" 27 | $COPY "${PWD}"/config.h "${DEST}/" 28 | $COPY "${PWD}"/sd_alloc.h "${DEST}/" 29 | $COPY "${PWD}"/serial_alloc.h "${DEST}/" 30 | $COPY "${PWD}"/serial_utils.hpp "${DEST}/" 31 | $COPY "${PWD}"/serial_utils.h "${DEST}/" 32 | $COPY "${PWD}"/spiram_alloc.h "${DEST}/" 33 | $COPY "${PWD}"/static_alloc.h "${DEST}/" 34 | $COPY "${PWD}"/utils.cpp "${DEST}/" 35 | $COPY "${PWD}"/utils.h "${DEST}/" 36 | $COPY "${PWD}"/virtmem.h "${DEST}/" 37 | $COPY "${PWD}"/vptr.h "${DEST}/" 38 | $COPY "${PWD}"/vptr_utils.h "${DEST}/" 39 | $COPY "${PWD}"/vptr_utils.hpp "${DEST}/" 40 | 41 | copyExample benchmark 42 | copyExample serial_simple 43 | copyExample serial_input 44 | copyExample spiram_simple 45 | copyExample multispiram_simple 46 | copyExample sd_simple 47 | copyExample alloc_properties 48 | copyExample locking 49 | -------------------------------------------------------------------------------- /virtmem/examples/serial_simple/serial_simple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal example showing how to use the serial virtual memory allocator 3 | * (SerialVAlloc/SerialVAllocP). This allocator uses RAM from an external device 4 | * connected through serial. 5 | * 6 | * The only requirement is that a so called 'RAM host' (for instance a PC) is 7 | * connected via the default serial port (Serial). The RAM host should run the 8 | * 'virtmem/extras/serial_host.py' Python script. 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | const uint32_t poolSize = 1024l * 32l; // the size of the virtual memory pool (in bytes) 17 | 18 | // pull in complete virtmem namespace 19 | using namespace virtmem; 20 | 21 | SerialVAlloc valloc(poolSize); // default settings: use Serial with 115200 baudrate 22 | 23 | // Example for using Serial1 with 9600 baudrate 24 | //SerialVAllocP valloc(poolSize, 9600, Serial1); 25 | 26 | void setup() 27 | { 28 | while (!Serial) 29 | ; // wait for serial to come up 30 | 31 | valloc.start(); 32 | } 33 | 34 | void loop() 35 | { 36 | // allocate some integer on virtual memory 37 | VPtr vpi = valloc.alloc(); 38 | 39 | *vpi = 42; // assign some value, just like a regular pointer! 40 | Serial.print("*vpi = "); Serial.println(*vpi); 41 | 42 | valloc.free(vpi); // And free the virtual memory 43 | 44 | delay(1000); // keep doing this with 1 second pauses inbetween... 45 | } 46 | -------------------------------------------------------------------------------- /virtmem/src/internal/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_UTILS_H 2 | #define VIRTMEM_UTILS_H 3 | 4 | #ifndef ARDUINO 5 | #include 6 | #define ASSERT assert 7 | #else 8 | #include 9 | 10 | #if 0 11 | // From https://forum.pjrc.com/threads/23256-Get-Free-Memory-for-Teensy-3-0?p=34242&viewfull=1#post34242 12 | inline void freeRam(const char *msg) 13 | { 14 | uint32_t stacktop; 15 | uint32_t heaptop; 16 | 17 | // current position of the stack. 18 | stacktop = (uint32_t)&stacktop; 19 | 20 | // current position of heap. 21 | void *top = malloc(1); 22 | heaptop = (uint32_t)top; 23 | free(top); 24 | 25 | // The difference is the free, available ram. 26 | Serial.print(msg); Serial.println(stacktop - heaptop); 27 | 28 | // return stacktop - heaptop; 29 | } 30 | #endif 31 | 32 | #if 0 33 | #define ASSERT(x) \ 34 | do \ 35 | { \ 36 | if ((x)) ; else \ 37 | { \ 38 | Serial.print("Assertion failed!: "); Serial.print(#x); Serial.print(" @ "); Serial.print(__FILE__); Serial.print(":"); Serial.println(__LINE__); \ 39 | while (true) \ 40 | ; \ 41 | } \ 42 | } \ 43 | while (false); 44 | #else 45 | #define ASSERT(x) 46 | #endif 47 | 48 | #endif 49 | 50 | namespace virtmem { 51 | 52 | namespace private_utils { 53 | 54 | template T minimal(const T &v1, const T &v2) { return (v1 < v2) ? v1 : v2; } 55 | template T maximal(const T &v1, const T &v2) { return (v1 > v2) ? v1 : v2; } 56 | 57 | template struct AntiConst { typedef T type; }; 58 | template struct AntiConst { typedef T type; }; 59 | 60 | } 61 | 62 | } 63 | 64 | #endif // VIRTMEM_UTILS_H 65 | -------------------------------------------------------------------------------- /virtmem/examples/spiram_simple/spiram_simple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal example showing how to use the SPI RAM virtual memory allocator 3 | * (SPIRAMVAlloc/SPIRAMVAllocP). The 23LC series from Microchip are supported. 4 | * 5 | * Requirements: 6 | * - the serialram library should be installed (https://github.com/rhelmus/serialram) 7 | * - the SRAM chip should be properly connected with SPI (the CS pin is configured below) 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // configuration of SRAM chip: a 23LC1024 chip is assumed here which has CS connected to pin 9 18 | const int chipSelect = 9; 19 | const int chipSize = 1024l * 128l; // 128 kB (=1 mbit) 20 | const bool largeAddressing = true; // false if chipsize <1 mbit 21 | const SerialRam::ESPISpeed spiSpeed = SerialRam::SPEED_FULL; 22 | 23 | // pull in complete virtmem namespace 24 | using namespace virtmem; 25 | 26 | SPIRAMVAlloc valloc(chipSize, largeAddressing, chipSelect, spiSpeed); 27 | 28 | void setup() 29 | { 30 | while (!Serial) 31 | ; // wait for serial to come up 32 | 33 | Serial.begin(115200); 34 | valloc.start(); 35 | 36 | delay(3000); // add some delay so the user can connect with a serial terminal 37 | } 38 | 39 | void loop() 40 | { 41 | // allocate some integer on virtual memory 42 | VPtr vpi = valloc.alloc(); 43 | 44 | *vpi = 42; // assign some value, just like a regular pointer! 45 | Serial.print("*vpi = "); Serial.println(*vpi); 46 | 47 | valloc.free(vpi); // And free the virtual memory 48 | 49 | delay(1000); // keep doing this with 1 second pauses inbetween... 50 | } 51 | -------------------------------------------------------------------------------- /virtmem/examples/sd_simple/sd_simple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal example showing how to use the SD virtual memory allocator 3 | * (SDVAlloc/SDVAllocP). This allocator uses a file on a FAT formatted SD card as RAM. 4 | * 5 | * Requirements: 6 | * - the SdFat library should be installed (https://github.com/greiman/SdFat) 7 | * - an FAT formatted SD card 8 | * - a connection to the SD card via SPI 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // configuration for SD 18 | const int chipSelect = 9; 19 | const uint32_t poolSize = 1024l * 32l; // the size of the virtual memory pool (in bytes) 20 | const int spiSpeed = SPI_FULL_SPEED; 21 | 22 | // pull in complete virtmem namespace 23 | using namespace virtmem; 24 | 25 | SdFat sd; 26 | SDVAlloc valloc(poolSize); 27 | 28 | void setup() 29 | { 30 | // uncomment if using the ethernet shield 31 | // pinMode(10, OUTPUT); digitalWrite(10, HIGH); 32 | 33 | while (!Serial) 34 | ; // wait for serial to come up 35 | 36 | Serial.begin(115200); 37 | 38 | // initialize SdFat library: this should be done before starting the allocator! 39 | if (!sd.begin(chipSelect, spiSpeed)) 40 | sd.initErrorHalt(); 41 | 42 | valloc.start(); 43 | 44 | delay(3000); // add some delay so the user can connect with a serial terminal 45 | } 46 | 47 | void loop() 48 | { 49 | // allocate some integer on virtual memory 50 | VPtr vpi = valloc.alloc(); 51 | 52 | *vpi = 42; // assign some value, just like a regular pointer! 53 | Serial.print("*vpi = "); Serial.println(*vpi); 54 | 55 | valloc.free(vpi); // And free the virtual memory 56 | 57 | delay(1000); // keep doing this with 1 second pauses inbetween... 58 | } 59 | -------------------------------------------------------------------------------- /virtmem/src/src.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2014-12-08T21:07:02 4 | # 5 | #------------------------------------------------- 6 | 7 | QT -= core gui 8 | 9 | TARGET = virtmem 10 | TEMPLATE = lib 11 | CONFIG += staticlib 12 | 13 | SOURCES += \ 14 | base_alloc.cpp \ 15 | utils.cpp \ 16 | 17 | HEADERS += \ 18 | virtmem.h \ 19 | internal/utils.h \ 20 | internal/base_alloc.h \ 21 | config/config.h \ 22 | alloc/stdio_alloc.h \ 23 | internal/alloc.h \ 24 | alloc/spiram_alloc.h \ 25 | alloc/static_alloc.h \ 26 | alloc/sd_alloc.h \ 27 | internal/vptr_utils.h \ 28 | internal/vptr.h \ 29 | internal/base_vptr.h \ 30 | internal/vptr_utils.hpp \ 31 | alloc/serial_alloc.h \ 32 | internal/serial_utils.h \ 33 | internal/serial_utils.hpp 34 | unix { 35 | target.path = /usr/lib 36 | INSTALLS += target 37 | } 38 | 39 | OTHER_FILES += \ 40 | ../examples/benchmark/benchmark.ino \ 41 | ../examples/benchmark/benchmark.h \ 42 | ../examples/serial_simple/serial_simple.ino \ 43 | ../examples/serial_input/serial_input.ino \ 44 | ../examples/spiram_simple/spiram_simple.ino \ 45 | ../examples/multispiram_simple/multispiram_simple.ino \ 46 | ../examples/sd_simple/sd_simple.ino \ 47 | ../examples/alloc_properties/alloc_properties.ino \ 48 | ../examples/locking/locking.ino 49 | 50 | QMAKE_CXXFLAGS_RELEASE += -Os 51 | #QMAKE_CXXFLAGS_DEBUG += -Og 52 | QMAKE_CXXFLAGS += -std=gnu++11 -ffunction-sections -fdata-sections 53 | QMAKE_LFLAGS += -Wl,--gc-sections 54 | 55 | OTHER_FILES += \ 56 | arduinofy.sh \ 57 | ../extras/serial_host.py \ 58 | ../extras/serialiohandler.py 59 | 60 | DISTFILES += \ 61 | ../../README.md \ 62 | ../TODO.txt \ 63 | ../../doc/manual.md \ 64 | ../../doc/Doxyfile 65 | -------------------------------------------------------------------------------- /virtmem/examples/multispiram_simple/multispiram_simple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Minimal example showing how to use the multi SPI RAM virtual memory allocator 3 | * (MultiSPIRAMVAllocP). The 23LC series from Microchip are supported. In this example 4 | * two chips are used. The total memory of both chips will be combined by the allocator. 5 | * 6 | * Requirements: 7 | * - the serialram library should be installed (https://github.com/rhelmus/serialram) 8 | * - two SRAM chips should be properly connected with SPI (the CS pins are configured below) 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // pull in complete virtmem namespace 19 | using namespace virtmem; 20 | 21 | // configuration for two 23LC1024 chips (128 kByte in size), CS connected to pins 9 & 10 22 | SPIRamConfig scfg[2] = 23 | { 24 | // format: (true if > 512 kbit), , , 25 | { true, 1024l * 128l, 9, SerialRam::SPEED_FULL }, 26 | { true, 1024l * 128l, 10, SerialRam::SPEED_FULL } 27 | }; 28 | 29 | typedef MultiSPIRAMVAllocP Alloc; // shortcut 30 | Alloc valloc; 31 | 32 | void setup() 33 | { 34 | while (!Serial) 35 | ; // wait for serial to come up 36 | 37 | Serial.begin(115200); 38 | valloc.start(); 39 | 40 | delay(3000); // add some delay so the user can connect with a serial terminal 41 | } 42 | 43 | void loop() 44 | { 45 | // allocate some integer on virtual memory 46 | VPtr vpi = valloc.alloc(); 47 | 48 | *vpi = 42; // assign some value, just like a regular pointer! 49 | Serial.print("*vpi = "); Serial.println(*vpi); 50 | 51 | valloc.free(vpi); // And free the virtual memory 52 | 53 | delay(1000); // keep doing this with 1 second pauses inbetween... 54 | } 55 | -------------------------------------------------------------------------------- /virtmem/src/alloc/static_alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_STATIC_ALLOC_H 2 | #define VIRTMEM_STATIC_ALLOC_H 3 | 4 | /** 5 | * @file 6 | * @brief This file contains the static virtual memory allocator (for debug) 7 | */ 8 | 9 | // allocator using static array; for testing 10 | 11 | #include 12 | 13 | #include "internal/alloc.h" 14 | 15 | namespace virtmem { 16 | 17 | /** 18 | * @brief Virtual memory allocator that uses a static array (in regular RAM) as memory pool. 19 | * 20 | * This allocator does not have any dependencies and is mainly provided for testing. 21 | * 22 | * @tparam poolSize The size of the memory pool. 23 | * @tparam Properties Allocator properties, see DefaultAllocProperties 24 | * 25 | * @note Since the pool size must be given when instantiating the allocator, the @ref setPoolSize 26 | * function is not available for this allocator. 27 | * 28 | * @sa @ref bUsing 29 | */ 30 | 31 | template 32 | class StaticVAllocP : public VAlloc > 33 | { 34 | char staticData[poolSize]; 35 | 36 | void doStart(void) { } 37 | void doSuspend(void) { } 38 | void doStop(void) { } 39 | 40 | void doRead(void *data, VPtrSize offset, VPtrSize size) 41 | { 42 | ::memcpy(data, &staticData[offset], size); 43 | } 44 | 45 | void doWrite(const void *data, VPtrSize offset, VPtrSize size) 46 | { 47 | ::memcpy(&staticData[offset], data, size); 48 | } 49 | 50 | using BaseVAlloc::setPoolSize; 51 | 52 | public: 53 | StaticVAllocP(void) { this->setPoolSize(poolSize); } //!< Constructs (but not initializes) the allocator. 54 | }; 55 | 56 | typedef StaticVAllocP<> StaticVAlloc; //!< Shortcut to StaticVAllocP with default template arguments 57 | 58 | } 59 | 60 | #endif // VIRTMEM_STATIC_ALLOC_H 61 | -------------------------------------------------------------------------------- /virtmem/examples/serial_input/serial_input.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example shows how to handle serial input when using the serial virtual memory allocator 3 | * (SerialVAlloc/SerialVAllocP) with a 'shared' serial port. In this example the default serial port, 4 | * ('Serial') is used. 5 | * 6 | * The only requirement is that a so called 'RAM host' (for instance a PC) is 7 | * connected via the default serial port (Serial). The RAM host should run the 8 | * 'virtmem/extras/serial_host.py' Python script. 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | const uint32_t poolSize = 1024l * 32l; // the size of the virtual memory pool (in bytes) 17 | 18 | // pull in complete virtmem namespace 19 | using namespace virtmem; 20 | 21 | SerialVAlloc valloc(poolSize); // default settings: use Serial with 115200 baudrate 22 | 23 | void setup() 24 | { 25 | while (!Serial) 26 | ; // wait for serial to come up 27 | 28 | valloc.start(); 29 | } 30 | 31 | void loop() 32 | { 33 | if (valloc.input.availableAtLeast() > 0) // some input available? 34 | { 35 | // get the amount of bytes available. 36 | // NOTE: this function is the more 'expensive', but accurate version of 37 | // availableAtLeast(). 38 | int bytes = valloc.input.available(); 39 | 40 | // read a single byte first 41 | Serial.print("read single byte: "); Serial.println(valloc.input.read()); 42 | --bytes; 43 | 44 | if (bytes > 0) // more input? 45 | { 46 | // read it in a buffer: reading it in one go is much faster 47 | char buffer[128]; 48 | 49 | bytes = max(bytes, 127); // make sure we don't read too much 50 | 51 | const int readbytes = valloc.input.readBytes(buffer, bytes); 52 | buffer[readbytes] = 0; // make sure the string is null terminated 53 | Serial.print("read string: "); Serial.println(buffer); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /virtmem/src/internal/serial_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_SERIAL_UTILS_H 2 | #define VIRTMEM_SERIAL_UTILS_H 3 | 4 | /** 5 | * @file 6 | * @brief This file contains utilities for the serial virtual memory allocator 7 | */ 8 | 9 | namespace virtmem { 10 | 11 | //! @brief Contains utilities for serial allocator 12 | namespace serram_utils { 13 | 14 | //! @cond HIDDEN_SYMBOLS 15 | enum 16 | { 17 | CMD_INIT = 0, 18 | CMD_INITPOOL, 19 | CMD_READ, 20 | CMD_WRITE, 21 | CMD_INPUTAVAILABLE, 22 | CMD_INPUTREQUEST, 23 | CMD_INPUTPEEK, 24 | CMD_PING 25 | }; 26 | //! @endcond 27 | 28 | /** 29 | * @brief Utility class that handles serial input over a port that is used by by SerialVAlloc 30 | */ 31 | template class SerialInput 32 | { 33 | uint32_t availableMin; 34 | IOStream *stream; 35 | 36 | public: 37 | SerialInput(IOStream *s) : availableMin(0), stream(s) { } 38 | 39 | /** 40 | * @brief Available bytes that can be read via serial 41 | * @return Number of bytes that can be read 42 | */ 43 | uint32_t available(void); 44 | 45 | /** 46 | * @brief Returns the minimum number of bytes that can be read. 47 | * @return The number of bytes that are minimally available to read. 48 | * @note This function tries to use cached data and is therefore often more efficient compared to available(). 49 | */ 50 | uint32_t availableAtLeast(void); 51 | 52 | /** 53 | * @brief Read a byte from serial input. 54 | * @return Returns the byte that was read, or -1 if none were available. 55 | */ 56 | int16_t read(void); 57 | 58 | /** 59 | * @brief Read multiple bytes from serial input 60 | * @param buffer Destination character array to read data into. 61 | * @param count Amount of bytes to read 62 | * @return Amount of bytes to read 63 | */ 64 | uint32_t readBytes(char *buffer, uint32_t count); 65 | 66 | /** 67 | * @brief Reads a byte from serial input without removing it from the serial buffer 68 | * @return Available byte to read, or -1 if none. 69 | */ 70 | int16_t peek(void); 71 | }; 72 | 73 | } 74 | 75 | } 76 | 77 | #include "serial_utils.hpp" 78 | 79 | #endif // VIRTMEM_SERIAL_UTILS_H 80 | -------------------------------------------------------------------------------- /virtmem/src/alloc/stdio_alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_STDIO_ALLOC_H 2 | #define VIRTMEM_STDIO_ALLOC_H 3 | 4 | /** 5 | * @file 6 | * @brief This file contains the stdio virtual memory allocator (for debug) 7 | */ 8 | 9 | #include "internal/alloc.h" 10 | #include "config/config.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace virtmem { 17 | 18 | /** 19 | * @brief Virtual memory allocator that uses a regular file (via stdio) as memory pool. 20 | * 21 | * This class is meant for debugging and can only be used on systems supporting stdio (e.g. PCs). 22 | * 23 | * @tparam Properties Allocator properties, see DefaultAllocProperties 24 | * 25 | * @sa @ref bUsing 26 | */ 27 | template 28 | class StdioVAllocP : public VAlloc > 29 | { 30 | FILE *ramFile; 31 | 32 | void doStart(void) 33 | { 34 | ramFile = tmpfile(); 35 | if (!ramFile) 36 | fprintf(stderr, "Unable to open ram file!"); 37 | this->writeZeros(0, this->getPoolSize()); // make sure it gets the right size 38 | } 39 | 40 | void doSuspend(void) { } 41 | void doStop(void) { if (ramFile) { fclose(ramFile); ramFile = 0; } } 42 | void doRead(void *data, VPtrSize offset, VPtrSize size) 43 | { 44 | if (fseek(ramFile, offset, SEEK_SET) != 0) 45 | fprintf(stderr, "fseek error: %s\n", strerror(errno)); 46 | 47 | fread(data, size, 1, ramFile); 48 | if (ferror(ramFile)) 49 | fprintf(stderr, "didn't read correctly: %s\n", strerror(errno)); 50 | 51 | } 52 | 53 | void doWrite(const void *data, VPtrSize offset, VPtrSize size) 54 | { 55 | if (fseek(ramFile, offset, SEEK_SET) != 0) 56 | fprintf(stderr, "fseek error: %s\n", strerror(errno)); 57 | 58 | fwrite(data, size, 1, ramFile); 59 | if (ferror(ramFile)) 60 | fprintf(stderr, "didn't write correctly: %s\n", strerror(errno)); 61 | } 62 | 63 | public: 64 | /** 65 | * @brief Constructs (but not initializes) the allocator. 66 | * @param ps Total amount of bytes of the memory pool. 67 | * @sa setPoolSize 68 | */ 69 | StdioVAllocP(VPtrSize ps=VIRTMEM_DEFAULT_POOLSIZE) : ramFile(0) { this->setPoolSize(ps); } 70 | ~StdioVAllocP(void) { doStop(); } 71 | }; 72 | 73 | typedef StdioVAllocP<> StdioVAlloc; //!< Shortcut to StdioVAllocP with default template arguments 74 | 75 | } 76 | 77 | #endif // VIRTMEM_STDIO_ALLOC_H 78 | -------------------------------------------------------------------------------- /virtmem/examples/alloc_properties/alloc_properties.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example showing how the internal memory pages of an allocator can be configured. 3 | * These allocator properties describe the size and amount of memory pages are used. Since they 4 | * are stored in regular RAM, configuring allocator properties can be useful to limit the RAM 5 | * used by virtmem. On the other hand, if you have RAM to spare, increasing the size and/or amount 6 | * of memory pages can speed up virtmem. Increasing the number of pages typically improves 7 | * random access speeds, while increasing the size of memory pages typically reduces the amount 8 | * of swapping needed. For more information refer to the documentation on DefaultAllocProperties. 9 | * 10 | * This example uses the SD allocator, however, any other allocator could be configured in a similar 11 | * way. 12 | * 13 | * Requirements: 14 | * - the SdFat library should be installed (https://github.com/greiman/SdFat) 15 | * - an FAT formatted SD card 16 | * - a connection to the SD card via SPI 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | // configuration for SD 25 | const int chipSelect = 9; 26 | const uint32_t poolSize = 1024l * 32l; // the size of the virtual memory pool (in bytes) 27 | const int spiSpeed = SPI_FULL_SPEED; 28 | 29 | // pull in complete virtmem namespace 30 | using namespace virtmem; 31 | 32 | // struct containing the properties used to define the memory pages of an allocator. 33 | // Note that all variables should be static and have a large enough (integer) type to contain the 34 | // numeric value. By default every allocator uses the values defined from DefaultAllocProperties, 35 | // defined in config.h. 36 | struct AllocProperties 37 | { 38 | static const uint8_t smallPageCount = 4, smallPageSize = 64; 39 | static const uint8_t mediumPageCount = 4, mediumPageSize = 128; 40 | static const uint8_t bigPageCount = 4; 41 | static const uint16_t bigPageSize = 512; // note: uint16_t to contain larger numeric value 42 | }; 43 | 44 | typedef SDVAllocP Alloc; // shortcut 45 | 46 | SdFat sd; 47 | Alloc valloc(poolSize); 48 | 49 | // rest is more or less the same as sd_simple example 50 | // ... 51 | 52 | void setup() 53 | { 54 | // uncomment if using the ethernet shield 55 | // pinMode(10, OUTPUT); digitalWrite(10, HIGH); 56 | 57 | while (!Serial) 58 | ; // wait for serial to come up 59 | 60 | Serial.begin(115200); 61 | 62 | // initialize SdFat library: this should be done before starting the allocator! 63 | if (!sd.begin(chipSelect, spiSpeed)) 64 | sd.initErrorHalt(); 65 | 66 | valloc.start(); 67 | 68 | delay(3000); // add some delay so the user can connect with a serial terminal 69 | } 70 | 71 | void loop() 72 | { 73 | // allocate some integer on virtual memory 74 | VPtr vpi = valloc.alloc(); 75 | 76 | *vpi = 42; // assign some value, just like a regular pointer! 77 | Serial.print("*vpi = "); Serial.println(*vpi); 78 | 79 | valloc.free(vpi); // And free the virtual memory 80 | 81 | delay(1000); // keep doing this with 1 second pauses inbetween... 82 | } 83 | -------------------------------------------------------------------------------- /virtmem/examples/locking/locking.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Example showing how to use virtual data locks. When a lock is made a portion of virtual memory 3 | * is kept within a virtual memory page until the lock is released. Locking data can significantly 4 | * improve performance since data can be accessed through a regular pointer, and helps dealing 5 | * with code that only accepts regular pointers. The latter is demonstarted in this example. 6 | * 7 | * This example uses the SD allocator, however, any other allocator could be used as well. 8 | * 9 | * Requirements: 10 | * - the SdFat library should be installed (https://github.com/greiman/SdFat) 11 | * - an FAT formatted SD card 12 | * - a connection to the SD card via SPI 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // configuration for SD 21 | const int chipSelect = 4; 22 | const uint32_t poolSize = 1024l * 328l; // the size of the virtual memory pool (in bytes) 23 | const int spiSpeed = SPI_FULL_SPEED; 24 | 25 | // pull in complete virtmem namespace 26 | using namespace virtmem; 27 | 28 | SdFat sd; 29 | SDVAlloc valloc(poolSize); 30 | 31 | void setup() 32 | { 33 | // uncomment if using the ethernet shield 34 | // pinMode(10, OUTPUT); digitalWrite(10, HIGH); 35 | 36 | while (!Serial) 37 | ; // wait for serial to come up 38 | 39 | Serial.begin(115200); 40 | 41 | // initialize SdFat library: this should be done before starting the allocator! 42 | if (!sd.begin(chipSelect, spiSpeed)) 43 | sd.initErrorHalt(); 44 | 45 | valloc.start(); 46 | 47 | delay(3000); // add some delay so the user can connect with a serial terminal 48 | 49 | Serial.println("Initialized!"); 50 | } 51 | 52 | void loop() 53 | { 54 | // allocate a string in virtual memory 55 | VPtr vstr = valloc.alloc(128); 56 | 57 | strcpy(vstr, "hello (virtual) world!"); 58 | 59 | // print the string: for this we want to use Serial.print(), however, this function 60 | // only accepts 'regular' strings. Hence, we have to lock the data first. 61 | 62 | int size = strlen(vstr) + 1; // size to lock (including string zero terminator) 63 | VPtr p = vstr; 64 | while (size) 65 | { 66 | // Make the lock: the last argument specifies the lock should be read-only (more efficient) 67 | // Since we only have to print the string we set this to true. 68 | VPtrLock > lock = makeVirtPtrLock(p, size, true); 69 | 70 | // Get the actual size of the lock. This is important, because it can happen that 71 | // the complete data could not be locked at once. 72 | const int lockedsize = lock.getLockSize(); 73 | 74 | // dereferencing the lock (*lock) will return a regular pointer to the data 75 | Serial.write(*lock, lockedsize); 76 | 77 | size -= lockedsize; 78 | p += lockedsize; 79 | 80 | // in case lockedsize was smaller than size, the above code will be called again using the 81 | // remaining string. 82 | } 83 | 84 | Serial.println(""); // end with a newline 85 | 86 | valloc.free(vstr); // And free the virtual memory 87 | 88 | delay(1000); // keep doing this with 1 second pauses inbetween... 89 | } 90 | -------------------------------------------------------------------------------- /virtmem/extras/serial_host.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # global modules 4 | import argparse 5 | import os 6 | import queue 7 | import serial 8 | import sys 9 | import threading 10 | 11 | # local modules 12 | import serialiohandler 13 | 14 | # --- config --- 15 | 16 | # Values here are used for argument parsing defaults. After parsing values are replaced. 17 | class Config: 18 | serialPort = 'COM3' if os.name == 'nt' else '/dev/ttyACM0' 19 | serialBaud = 115200 20 | serialInitValue = 0xFF 21 | serialPassDev = None 22 | serialPassBaud = 115200 23 | 24 | # --- 25 | 26 | doQuit = False 27 | inputQueue = queue.Queue() 28 | serPassInterface = None 29 | 30 | def printPrompt(): 31 | print("> ", end="", flush=True) 32 | 33 | def checkCommandArguments(): 34 | parser = argparse.ArgumentParser() 35 | parser.add_argument("-p", "--port", help="serial device connected to the arduino. Default: %(default)s", 36 | default=Config.serialPort) 37 | parser.add_argument("-b", "--baud", help="Serial baudrate. Default: %(default)d", type=int, 38 | default=Config.serialBaud) 39 | parser.add_argument("-l", "--pass", help="serial pass through device", 40 | default=Config.serialPassDev, dest='passdev') 41 | parser.add_argument("-r", "--passbaud", help="baud rate of serial port pass through device. Default: %(default)d", 42 | type=int, default=Config.serialPassBaud) 43 | 44 | # Update configs 45 | args = parser.parse_args() 46 | Config.serialPort = args.port 47 | Config.serialBaud = args.baud 48 | Config.serialPassDev = args.passdev 49 | Config.serialPassBaud = args.passbaud 50 | 51 | def updateSerial(): 52 | while not doQuit: 53 | serialiohandler.update() 54 | try: 55 | serialiohandler.processInput(inputQueue.get(False)) 56 | except queue.Empty: 57 | pass 58 | 59 | def monitorInput(): 60 | try: 61 | if Config.serialPassDev: 62 | while True: 63 | line = serPassInterface.readline() 64 | if line: 65 | inputQueue.put(line) 66 | else: 67 | for line in sys.stdin: 68 | inputQueue.put(bytearray(line, 'ascii')) 69 | printPrompt() 70 | except KeyboardInterrupt: 71 | pass 72 | 73 | # if we are here the user sent an EOF (e.g. ctrl+D) or aborted (e.g. ctrl+C) 74 | 75 | def init(): 76 | checkCommandArguments() 77 | 78 | if Config.serialPassDev: 79 | global serPassInterface 80 | serPassInterface = serial.Serial(port=Config.serialPassDev, baudrate=Config.serialPassBaud, timeout=50) 81 | serialiohandler.connect(Config.serialPort, Config.serialBaud, Config.serialInitValue, serPassInterface) 82 | else: 83 | serialiohandler.connect(Config.serialPort, Config.serialBaud, Config.serialInitValue, sys.stdout.buffer) 84 | 85 | print("Monitoring serial port {}. Press ctrl+C to quit.".format(Config.serialPort)) 86 | printPrompt() 87 | 88 | global serIOThread 89 | serIOThread = threading.Thread(target = updateSerial) 90 | serIOThread.start() 91 | 92 | def main(): 93 | init() 94 | monitorInput() 95 | global doQuit 96 | doQuit = True 97 | serialiohandler.quit() 98 | serIOThread.join() 99 | 100 | if __name__ == "__main__": 101 | main() 102 | -------------------------------------------------------------------------------- /virtmem/src/alloc/sd_alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_SD_ALLOC_H 2 | #define VIRTMEM_SD_ALLOC_H 3 | 4 | /** 5 | * @file 6 | * @brief This file contains the SD fat virtual memory allocator 7 | */ 8 | 9 | #include "internal/alloc.h" 10 | 11 | #include 12 | 13 | namespace virtmem { 14 | 15 | /** 16 | * @brief Virtual memory allocator class that uses SD card as virtual pool 17 | * 18 | * This class uses a file on an FAT formatted SD card as virtual memory pool. The 19 | * [SD FAT library](https://github.com/greiman/SdFat) is used to interface with the SD card 20 | * and therefore has to be installed. 21 | * 22 | * When the allocator is initialized (i.e. by calling start()) it will create a file called 23 | * 'ramfile.vm' in the root directory. Existing files will be reused and resized if necessary. 24 | * 25 | * @tparam Properties Allocator properties, see DefaultAllocProperties 26 | * 27 | * @note The SD FAT library needs to be initialized (i.e. by calling SdFat::begin()) *before* 28 | * initializing this allocator. 29 | * @sa @ref bUsing, SDVAlloc 30 | */ 31 | template 32 | class SDVAllocP : public VAlloc > 33 | { 34 | SdFile sdFile; 35 | 36 | void doStart(void) 37 | { 38 | // file does not exist yet (can we create it)? 39 | if (sdFile.open("ramfile.vm", O_CREAT | O_EXCL)) 40 | this->writeZeros(0, this->getPoolSize()); // make it the right size 41 | else // already exists, check size 42 | { 43 | if (!sdFile.open("ramfile.vm", O_CREAT | O_RDWR)) 44 | { 45 | Serial.println("opening ram file failed"); 46 | while (true) 47 | ; 48 | } 49 | 50 | const uint32_t size = sdFile.fileSize(); 51 | if (size < this->getPoolSize()) 52 | this->writeZeros(size, this->getPoolSize() - size); 53 | } 54 | } 55 | 56 | void doStop(void) 57 | { 58 | sdFile.close(); 59 | } 60 | void doRead(void *data, VPtrSize offset, VPtrSize size) 61 | { 62 | // const uint32_t t = micros(); 63 | sdFile.seekSet(offset); 64 | sdFile.read(data, size); 65 | // Serial.print("read: "); Serial.print(size); Serial.print("/"); Serial.println(micros() - t); 66 | } 67 | 68 | void doWrite(const void *data, VPtrSize offset, VPtrSize size) 69 | { 70 | // const uint32_t t = micros(); 71 | sdFile.seekSet(offset); 72 | sdFile.write(data, size); 73 | // Serial.print("write: "); Serial.print(size); Serial.print("/"); Serial.println(micros() - t); 74 | } 75 | 76 | public: 77 | /** Constructs (but not initializes) the SD FAT allocator. 78 | * @param ps The size of the virtual memory pool 79 | * @sa setPoolSize 80 | */ 81 | SDVAllocP(VPtrSize ps=VIRTMEM_DEFAULT_POOLSIZE) { this->setPoolSize(ps); } 82 | ~SDVAllocP(void) { doStop(); } 83 | 84 | /** 85 | * Removes the temporary file used as virtual memory pool. 86 | * @note Only call this when the allocator is not initialized! 87 | */ 88 | void removeTempFile(void) { sdFile.remove(); } 89 | }; 90 | 91 | typedef SDVAllocP<> SDVAlloc; //!< Shortcut to SDVAllocP with default template arguments 92 | 93 | /** 94 | * @example sd_simple.ino 95 | * This is a simple example sketch showing how to use the SD allocator. 96 | */ 97 | 98 | } 99 | 100 | #endif // VIRTMEM_SD_ALLOC_H 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Main 2 | ========= 3 | 4 | # Introduction 5 | `virtmem` is an Arduino library that allows your project to easily use an 6 | external memory source to extend the (limited) amount of available RAM. This 7 | library supports several memory resources, for instance, SPI ram (e.g. the 8 | `23LC1024` chip from Microchip), an SD card or even a computer connected via a 9 | serial connection. The library is made in such a way that managing and using 10 | this _virtual memory_ closely resembles working with data from 'normal' memory. 11 | 12 | # Features 13 | * Extend the available memory with kilobytes, megabytes or even gigabytes 14 | * Supports SPI RAM (23LC series from Microchip), SD cards and RAM from a computer connected through serial 15 | * Easy C++ interface that closely resembles regular data access 16 | * Memory page system to speed up access to virtual memory 17 | * New memory interfaces can be added easily 18 | * Code is mostly platform independent and can fairly easy be ported to other 19 | plaforms (x86 port exists for debugging) 20 | 21 | # Demonstration 22 | ~~~{.cpp} 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | // Simplify virtmem usage 29 | using namespace virtmem; 30 | 31 | // Create virtual a memory allocator that uses SD card (with FAT filesystem) as virtual memory pool 32 | // The default memory pool size (1 MB) is used. 33 | SDVAlloc valloc; 34 | 35 | SdFat sd; 36 | 37 | struct MyStruct { int x, y; }; 38 | 39 | void setup() 40 | { 41 | // Initialize SdFatlib 42 | if (!sd.begin(9, SPI_FULL_SPEED)) 43 | sd.initErrorHalt(); 44 | 45 | valloc.start(); // Always call this to initialize the allocator before using it 46 | 47 | // Allocate a char buffer of 10000 bytes in virtual memory and store the address to a virtual pointer 48 | VPtr str = valloc.alloc(10000); 49 | 50 | // Set the first 1000 bytes to 'A' 51 | memset(str, 'A', 1000); 52 | 53 | // array access 54 | str[1] = 'B'; 55 | 56 | // Own types (structs/classes) also work. 57 | VPtr ms = valloc.alloc(); // alloc call without parameters: use automatic size deduction 58 | ms->x = 5; 59 | ms->y = 15; 60 | } 61 | 62 | void loop() 63 | { 64 | // ... 65 | } 66 | ~~~ 67 | 68 | This Arduino sketch demonstrates how to use a SD card as virtual memory 69 | store. By using a virtual memory pointer wrapper class, using virtual memory 70 | becomes quite close to using data residing in 'normal' memory. 71 | 72 | # Manual 73 | The manual [can be found here](http://rhelmus.github.io/virtmem/index.html). 74 | 75 | # Benchmark 76 | Some benchmarking results are shown below. Note that these numbers are generated with very simple, 77 | and possibly not so accurate tests, hence they should only be used as a rough indication. 78 | 79 | 80 | 81 | 86 | 91 | 96 |
Read / Write speed (kB/s) 82 | Teensy 3.2 (96 MHz) 83 | Teensy 3.2 (144 MHz) 84 | Arduino Uno 85 |
Serial 87 | 500 / 373 88 | 496 / 378 89 | 30 / 20 90 |
SD 92 | 1107 / 98 93 | 1102 / 91 94 | 156 / 44 95 |
SPI RAM 97 | 1887 / 1159 98 | 2083 / 1207 99 | 150 / 118 100 |
101 | 102 | Some notes: 103 | - Serial: Virtual means that a USB serial connection is used, which is only limited by USB speeds. 104 | - SD/SPI RAM: measured at maximum SPI speeds. For SPI RAM a 23LCV1024 chip was used. 105 | - More details in [the manual](http://rhelmus.github.io/virtmem/index.html#bench). 106 | -------------------------------------------------------------------------------- /virtmem/examples/benchmark/benchmark.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "benchmark.h" 4 | 5 | //#define RUN_STATICALLOC 6 | //#define RUN_SPIRAMALLOC 7 | //#define RUN_NATIVE 8 | //#define RUN_SERIALALLOC 9 | #define RUN_SDALLOC 10 | 11 | // uncomment to disable a SPI select pin, useful when using ethernet shield 12 | #define DISABLE_SELECTPIN1 7 13 | #define DISABLE_SELECTPIN2 8 14 | #define DISABLE_SELECTPIN3 9 15 | #define DISABLE_SELECTPIN4 10 16 | 17 | #define NATIVE_BUFSIZE 1024 * 1 18 | #define NATIVE_REPEATS 100 19 | 20 | #define STATICALLOC_POOLSIZE 1024l * 1l + 128l // plus some size overhead 21 | #define STATICALLOC_BUFSIZE 1024l * 127l 22 | #define STATICALLOC_REPEATS 100 23 | 24 | #define SPIRAM_POOLSIZE 1024l * 128l 25 | #define SPIRAM_BUFSIZE 1024l * 12l 26 | #define SPIRAM_REPEATS 5 27 | #define SPIRAM_CSPIN 9 28 | 29 | #define SERIALRAM_POOLSIZE 1024l * 1024l 30 | #define SERIALRAM_BUFSIZE 1024l * 12l 31 | #define SERIALRAM_REPEATS 5 32 | 33 | #define SD_POOLSIZE 1024l * 1024l 34 | #define SD_BUFSIZE 1024l * 12l 35 | #define SD_REPEATS 5 36 | #define SD_CSPIN 4 37 | #define SD_SPISPEED SPI_FULL_SPEED 38 | 39 | 40 | #ifdef RUN_STATICALLOC 41 | #include 42 | StaticVAllocP staticAlloc; 43 | #endif 44 | 45 | #ifdef RUN_SPIRAMALLOC 46 | #include 47 | #include 48 | #include 49 | SPIRAMVAlloc SPIRamAlloc(SPIRAM_POOLSIZE, true, SPIRAM_CSPIN, SerialRam::SPEED_FULL); 50 | #endif 51 | 52 | #ifdef RUN_SERIALALLOC 53 | #include 54 | SerialVAlloc serialRamAlloc(SERIALRAM_POOLSIZE, /*115200*/1000000); 55 | #endif 56 | 57 | #ifdef RUN_SDALLOC 58 | #include 59 | #include 60 | SDVAlloc SDRamAlloc(SD_POOLSIZE); 61 | SdFat sd; 62 | #endif 63 | 64 | 65 | #ifdef RUN_NATIVE 66 | void runNativeBenchmark(uint32_t bufsize, uint8_t repeats) 67 | { 68 | volatile char buf[bufsize]; 69 | 70 | printBenchStart(bufsize, repeats); 71 | 72 | const uint32_t time = millis(); 73 | for (uint8_t i=0; i void printVAStats(TA &valloc) 23 | { 24 | #ifdef VIRTMEM_TRACE_STATS 25 | Serial.println("allocator stats:"); 26 | Serial.print("Page reads:"); Serial.println(valloc.getBigPageReads()); 27 | Serial.print("Page writes:"); Serial.println(valloc.getBigPageWrites()); 28 | Serial.print("kB read:"); Serial.println(valloc.getBytesRead() / 1024); 29 | Serial.print("kB written:"); Serial.println(valloc.getBytesWritten() / 1024); 30 | valloc.resetStats(); 31 | #endif 32 | } 33 | 34 | template void runBenchmarks(TA &valloc, uint32_t bufsize, uint8_t repeats) 35 | { 36 | typedef VPtr TVirtPtr; 37 | valloc.start(); 38 | 39 | TVirtPtr buf = valloc.template alloc(bufsize); 40 | 41 | printBenchStart(bufsize, repeats); 42 | 43 | uint32_t time = millis(); 44 | for (uint8_t i=0; i l = makeVirtPtrLock(p, lsize); 87 | lsize = l.getLockSize(); 88 | char *b = *l; 89 | for (uint16_t j=0; j l = makeVirtPtrLock(p, lsize, true); 112 | lsize = l.getLockSize(); 113 | char *b = *l; 114 | for (uint16_t j=0; j 0: 25 | bytes = serInterface.read(size) 26 | if bytes: 27 | ret += bytes 28 | size -= len(bytes) 29 | return ret 30 | 31 | def readInt(): 32 | return struct.unpack('i', blockedRead(4))[0] 33 | 34 | def writeInt(i): 35 | serInterface.write(struct.pack('i', i)) 36 | 37 | def sendCommand(cmd): 38 | serInterface.write(bytes([State.initValue])) 39 | serInterface.write(bytes([cmd])) 40 | #print("send: ", bytes([State.initValue]), "/", bytes([cmd])) 41 | 42 | def processByte(byte, printunknown=True): 43 | val = ord(byte) 44 | if State.processState == 'gotinit': 45 | handleCommand(val) 46 | State.processState = 'idle' 47 | elif val == State.initValue: 48 | assert(State.processState == 'idle') 49 | # print("Got init!") 50 | State.processState = 'gotinit' 51 | else: 52 | assert(State.processState == 'idle') 53 | if printunknown: 54 | # State.outdev.write(byte.decode('ascii', errors='ignore')) 55 | State.outdev.write(byte) 56 | State.outdev.flush() 57 | 58 | def handleCommand(command): 59 | #print("command: ", command) 60 | if command == Commands.ping: 61 | sendCommand(Commands.ping) 62 | elif command == Commands.init: 63 | State.initialized = True 64 | State.memoryPool = None # remove pool 65 | sendCommand(Commands.init) # reply 66 | elif not State.initialized: 67 | pass 68 | elif command == Commands.initPool: 69 | State.memoryPool = bytearray(readInt()) 70 | print("set memory pool:", len(State.memoryPool), flush=True) 71 | elif command == Commands.inputAvailable: 72 | with State.inputLock: 73 | writeInt(len(State.inputData)) 74 | #print("request count: ", len(State.inputData)) 75 | elif command == Commands.inputRequest: 76 | with State.inputLock: 77 | count = min(readInt(), len(State.inputData)) 78 | writeInt(count) 79 | #print("Input request: {} ({})".format(State.inputData[:count], count), flush=True) 80 | serInterface.write(State.inputData[:count]) 81 | del State.inputData[:count] 82 | elif command == Commands.inputPeek: 83 | if len(State.inputData) == 0: 84 | serInterface.write(0) 85 | else: 86 | with State.inputLock: 87 | serInterface.write(1) 88 | serInterface.write(State.inputData[0]) 89 | elif State.memoryPool == None: 90 | print("WARNING: tried to read/write unitialized memory pool") 91 | elif command == Commands.read: 92 | index, size = readInt(), readInt() 93 | serInterface.write(State.memoryPool[index:size+index]) 94 | # print("read memPool: ", State.memoryPool[index:size+index]) 95 | # print("read memPool: ", index, size) 96 | elif command == Commands.write: 97 | index, size = readInt(), readInt() 98 | State.memoryPool[index:size+index] = blockedRead(size) 99 | # print("write memPool: ", State.memoryPool) 100 | # print("write memPool: ", index, size) 101 | 102 | def ensureConnection(): 103 | print("Waiting until port {} can be opened...\n".format(serInterface.port)) 104 | while not State.doQuit: 105 | try: 106 | serInterface.open() 107 | break 108 | except OSError: 109 | time.sleep(0.5) 110 | 111 | if State.doQuit: 112 | return 113 | 114 | time.sleep(1) # wait to settle after open (only needed if board resets) 115 | 116 | print("Connected and initialized!") 117 | 118 | def connect(port, baud, initval, outdev): 119 | serInterface.port = port 120 | serInterface.baudrate = baud 121 | serInterface.timeout = 0 122 | 123 | State.initValue = initval 124 | State.outdev = outdev 125 | 126 | ensureConnection() 127 | 128 | def update(): 129 | global serInterface 130 | 131 | try: 132 | byte = serInterface.read(1) 133 | while byte: 134 | processByte(byte) 135 | byte = serInterface.read(1) 136 | # NOTE: catch for TypeError as workaround for indexing bug in PySerial 137 | except (serial.serialutil.SerialException, TypeError): 138 | print("Caught serial exception, port disconnected?") 139 | State.initialized = False 140 | p, b = serInterface.port, serInterface.baudrate 141 | serInterface.close() 142 | serInterface = serial.Serial() 143 | connect(p, b, State.initValue, State.outdev) 144 | 145 | def processInput(line): 146 | #print("Sending input line:", line, flush=True) 147 | with State.inputLock: 148 | State.inputData += line 149 | 150 | def quit(): 151 | State.doQuit = True 152 | -------------------------------------------------------------------------------- /virtmem/src/internal/serial_utils.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "serial_utils.h" 4 | 5 | //! @cond HIDDEN_SYMBOLS 6 | 7 | namespace virtmem { 8 | 9 | namespace serram_utils { 10 | 11 | enum { CMD_START = 0xFF }; 12 | 13 | template void purgeSerial(IOStream *stream) 14 | { 15 | uint32_t n; 16 | while ((n = stream->available())) 17 | { 18 | for (; n; --n) 19 | stream->read(); 20 | } 21 | } 22 | 23 | template void writeUInt32(IOStream *stream, uint32_t i) 24 | { 25 | stream->write(i & 0xFF); 26 | stream->write((i >> 8) & 0xFF); 27 | stream->write((i >> 16) & 0xFF); 28 | stream->write((i >> 24) & 0xFF); 29 | } 30 | 31 | template void writeBlock(IOStream *stream, const uint8_t *data, uint32_t size) 32 | { 33 | stream->write(data, size); 34 | } 35 | 36 | template uint32_t readUInt32(IOStream *stream) 37 | { 38 | uint8_t i = 0; 39 | uint32_t ret = 0; 40 | while (i < 4) 41 | { 42 | if (stream->available()) 43 | { 44 | ret |= (stream->read() << (i * 8)); 45 | ++i; 46 | } 47 | } 48 | return ret; 49 | } 50 | 51 | template uint16_t readUInt16(IOStream *stream) 52 | { 53 | while (true) 54 | { 55 | if (stream->available() >= 2) 56 | return stream->read() | (stream->read() << 8); 57 | } 58 | } 59 | 60 | template uint8_t readUInt8(IOStream *stream) 61 | { 62 | while (!stream->available()) 63 | ; 64 | return stream->read(); 65 | } 66 | 67 | template void readBlock(IOStream *stream, char *data, uint32_t size) 68 | { 69 | while (size) 70 | size -= stream->readBytes(data, size); 71 | } 72 | 73 | template void sendWriteCommand(IOStream *stream, uint8_t cmd) 74 | { 75 | stream->write(CMD_START); 76 | stream->write(cmd); 77 | } 78 | 79 | template void sendReadCommand(IOStream *stream, uint8_t cmd) 80 | { 81 | purgeSerial(stream); 82 | stream->write(CMD_START); 83 | stream->write(cmd); 84 | } 85 | 86 | template bool waitForCommand(IOStream *stream, uint8_t cmd, uint8_t timeout) 87 | { 88 | stream->flush(); 89 | const uint32_t endtime = millis() + timeout; 90 | 91 | bool gotinit = false; 92 | while (millis() < endtime) 93 | { 94 | while (Serial.available()) 95 | { 96 | const uint8_t b = stream->read(); 97 | 98 | if (!gotinit && b == CMD_START) 99 | gotinit = true; 100 | else if (gotinit && b == cmd) 101 | return true; 102 | } 103 | } 104 | 105 | return false; 106 | } 107 | 108 | template void init(IOStream *stream, uint32_t baud, uint32_t poolsize) 109 | { 110 | stream->begin(baud); 111 | 112 | // handshake 113 | while (true) 114 | { 115 | sendWriteCommand(stream, CMD_INIT); 116 | if (waitForCommand(stream, CMD_INIT, 50)) 117 | break; 118 | } 119 | #if 0 120 | waitForCommand(stream, CMD_INIT); 121 | sendWriteCommand(stream, CMD_INIT); // reply 122 | #endif 123 | 124 | // Purge any remaining handshake responses from serial script. For some reason, 125 | // a simple delay and then purging is not enough, we have to actually use the serial 126 | // during the wait period to really get everything out. 127 | const uint32_t endtime = millis() + 75; 128 | while (millis() < endtime) 129 | purgeSerial(stream); 130 | 131 | sendWriteCommand(stream, CMD_INITPOOL); 132 | writeUInt32(stream, poolsize); 133 | stream->flush(); 134 | } 135 | 136 | 137 | template uint32_t SerialInput::available() 138 | { 139 | sendReadCommand(stream, CMD_INPUTAVAILABLE); 140 | stream->flush(); 141 | return readUInt32(stream); 142 | } 143 | 144 | template uint32_t SerialInput::availableAtLeast() 145 | { 146 | if (availableMin == 0) 147 | availableMin = available(); 148 | return availableMin; 149 | } 150 | 151 | template int16_t SerialInput::read() 152 | { 153 | sendReadCommand(stream, CMD_INPUTREQUEST); 154 | writeUInt32(stream, 1); 155 | stream->flush(); 156 | 157 | if (readUInt32(stream) == 0) 158 | return -1; // no data 159 | 160 | if (availableMin) 161 | --availableMin; 162 | return readUInt8(stream); 163 | } 164 | 165 | template uint32_t SerialInput::readBytes(char *buffer, uint32_t count) 166 | { 167 | sendReadCommand(stream, CMD_INPUTREQUEST); 168 | writeUInt32(stream, count); 169 | stream->flush(); 170 | count = readUInt32(stream); 171 | for (uint32_t i=0; i count) 174 | availableMin -= count; 175 | else 176 | availableMin = 0; 177 | return count; 178 | } 179 | 180 | template int16_t SerialInput::peek() 181 | { 182 | sendReadCommand(stream, CMD_INPUTPEEK); 183 | stream->flush(); 184 | if (readUInt8(stream) == 0) 185 | return -1; // nothing there 186 | return readUInt8(stream); 187 | } 188 | 189 | 190 | } 191 | 192 | } 193 | 194 | //! @endcond 195 | -------------------------------------------------------------------------------- /test/test_alloc.cpp: -------------------------------------------------------------------------------- 1 | #include "virtmem.h" 2 | #include "alloc/stdio_alloc.h" 3 | #include "test.h" 4 | 5 | #include 6 | 7 | 8 | TEST_F(VAllocFixture, SimpleAllocTest) 9 | { 10 | const VPtrNum ptr = valloc.allocRaw(sizeof(int)); 11 | ASSERT_NE(ptr, 0); 12 | 13 | int val = 55; 14 | valloc.write(ptr, &val, sizeof(val)); 15 | EXPECT_EQ(*(int *)valloc.read(ptr, sizeof(val)), val); 16 | valloc.flush(); 17 | EXPECT_EQ(*(int *)valloc.read(ptr, sizeof(val)), val); 18 | valloc.clearPages(); 19 | EXPECT_EQ(*(int *)valloc.read(ptr, sizeof(val)), val); 20 | 21 | valloc.freeRaw(ptr); 22 | } 23 | 24 | TEST_F(VAllocFixture, ReadOnlyTest) 25 | { 26 | const VPtrNum ptr = valloc.allocRaw(sizeof(int)); 27 | int val = 55; 28 | valloc.write(ptr, &val, sizeof(val)); 29 | valloc.flush(); 30 | 31 | // Read only read, shouldn't modify actual memory 32 | int *pval = (int *)valloc.read(ptr, sizeof(val)); 33 | *pval = 66; 34 | valloc.flush(); 35 | EXPECT_EQ(*(int *)valloc.read(ptr, sizeof(val)), 66); 36 | valloc.clearPages(); 37 | EXPECT_EQ(*(int *)valloc.read(ptr, sizeof(val)), val); 38 | } 39 | 40 | TEST_F(VAllocFixture, MultiAllocTest) 41 | { 42 | std::vector ptrlist; 43 | for (int i=0; i<(int)valloc.getBigPageCount(); ++i) 44 | { 45 | ptrlist.push_back(valloc.allocRaw(valloc.getBigPageSize())); 46 | valloc.write(ptrlist[i], &i, sizeof(int)); 47 | EXPECT_EQ(*(int *)valloc.read(ptrlist[i], sizeof(int)), i); 48 | } 49 | 50 | for (int i=0; i<(int)valloc.getBigPageCount(); ++i) 51 | { 52 | EXPECT_EQ(*(int *)valloc.read(ptrlist[i], sizeof(int)), i); 53 | } 54 | 55 | valloc.clearPages(); 56 | 57 | for (int i=0; i<(int)valloc.getBigPageCount(); ++i) 58 | { 59 | EXPECT_EQ(*(int *)valloc.read(ptrlist[i], sizeof(int)), i); 60 | } 61 | } 62 | 63 | TEST_F(VAllocFixture, SimplePageTest) 64 | { 65 | EXPECT_EQ(valloc.getFreeBigPages(), valloc.getBigPageCount()); 66 | 67 | std::vector ptrlist; 68 | for (int i=0; i<(int)valloc.getBigPageCount(); ++i) 69 | { 70 | ptrlist.push_back(valloc.allocRaw(valloc.getBigPageSize())); 71 | valloc.write(ptrlist[i], &i, sizeof(int)); 72 | } 73 | 74 | EXPECT_EQ(valloc.getFreeBigPages(), 0); 75 | 76 | valloc.flush(); 77 | 78 | for (int i=0; i<(int)valloc.getBigPageCount(); ++i) 79 | { 80 | EXPECT_EQ(*(int *)valloc.read(ptrlist[i], sizeof(int)), i); 81 | } 82 | 83 | valloc.clearPages(); 84 | EXPECT_EQ(valloc.getFreeBigPages(), valloc.getBigPageCount()); 85 | 86 | for (int i=0; i<(int)valloc.getBigPageCount(); ++i) 87 | { 88 | EXPECT_EQ(*(int *)valloc.read(ptrlist[i], sizeof(int)), i); 89 | } 90 | } 91 | 92 | TEST_F(VAllocFixture, PageLockTest) 93 | { 94 | EXPECT_EQ(valloc.getUnlockedSmallPages(), valloc.getSmallPageCount()); 95 | EXPECT_EQ(valloc.getUnlockedMediumPages(), valloc.getMediumPageCount()); 96 | EXPECT_EQ(valloc.getUnlockedBigPages(), valloc.getBigPageCount()); 97 | 98 | // 10 is an arbitrary number, just make sure that numbers are unique, don't start at the beginning 99 | // and don't overlap 100 | auto genptr = [this](uint8_t p) { return p * valloc.getBigPageSize() + 10; }; 101 | 102 | // Lock all big pages 103 | for (uint8_t p=0; prandom_seed()); 167 | std::vector buffer; 168 | buffer.reserve(size); 169 | for (size_t s=0; s(bufsize); 20 | UCharVirtPtr vbuf2 = VAllocFixture::valloc.alloc(bufsize); 21 | 22 | uint8_t buf1[bufsize], buf2[bufsize]; 23 | 24 | for (int i=0; i(bufsize); 57 | UCharVirtPtr vbuf2 = VAllocFixture::valloc.alloc(bufsize); 58 | uint8_t buf[bufsize]; 59 | 60 | for (int i=0; i buf; 83 | 84 | buf.reserve(bufsize); 85 | for (int i=0; i(bufsize); 89 | memcpy(vbuf, &buf[0], bufsize); 90 | valloc.clearPages(); 91 | ASSERT_EQ(memcmp(&buf[0], vbuf, bufsize), 0); 92 | 93 | UCharVirtPtr vbuf2 = VAllocFixture::valloc.alloc(bufsize); 94 | memcpy(vbuf2, vbuf, bufsize); 95 | valloc.clearPages(); 96 | EXPECT_EQ(memcmp(vbuf, vbuf2, bufsize), 0); 97 | } 98 | 99 | TEST_F(UtilsFixture, memsetTest) 100 | { 101 | const int bufsize = valloc.getBigPageSize() * 3; 102 | const uint8_t fill = 'A'; 103 | 104 | UCharVirtPtr vbuf = VAllocFixture::valloc.alloc(bufsize); 105 | EXPECT_EQ(memset(vbuf, fill, bufsize), vbuf); 106 | 107 | valloc.clearPages(); 108 | 109 | std::vector buf(bufsize, fill); 110 | EXPECT_EQ(memcmp(vbuf, &buf[0], bufsize), 0); 111 | } 112 | 113 | TEST_F(UtilsFixture, memcpyLargeMultiAllocTest) 114 | { 115 | // Second allocator 116 | typedef StaticVAllocP<1024*1024> Alloc2; 117 | Alloc2 valloc2; 118 | valloc2.start(); 119 | 120 | const int bufsize = valloc2.getPoolSize() / 2; 121 | 122 | UCharVirtPtr vbuf1 = VAllocFixture::valloc.alloc(bufsize); 123 | VPtr vbuf2 = valloc2.alloc(bufsize); 124 | 125 | memset(vbuf1, 'A', bufsize); 126 | memcpy(vbuf2, vbuf1, bufsize); 127 | EXPECT_EQ(memcmp(vbuf1, vbuf2, bufsize), 0); 128 | } 129 | 130 | TEST_F(UtilsFixture, strlenTest) 131 | { 132 | const int strsize = 10; 133 | CharVirtPtr vstr = VAllocFixture::valloc.alloc(strsize); 134 | 135 | vstr[0] = 0; 136 | valloc.clearPages(); 137 | EXPECT_EQ(strlen(vstr), 0); 138 | 139 | memset(vstr, 'A', strsize-1); 140 | vstr[strsize-1] = 0; 141 | valloc.clearPages(); 142 | EXPECT_EQ(strlen(vstr), strsize-1); 143 | 144 | vstr[5] = 0; 145 | EXPECT_EQ(strlen(vstr), 5); 146 | 147 | vstr[1] = 0; 148 | EXPECT_EQ(strlen(vstr), 1); 149 | } 150 | 151 | TEST_F(UtilsFixture, strncpyTest) 152 | { 153 | const int strsize = 10; 154 | CharVirtPtr vstr = VAllocFixture::valloc.alloc(strsize); 155 | 156 | char str[strsize] = "Howdy!", str2[strsize]; 157 | EXPECT_EQ(strncpy(vstr, str, strsize), vstr); 158 | EXPECT_EQ(strncpy(str2, vstr, strsize), str2); 159 | EXPECT_EQ(strncmp(str, str2, strsize), 0); 160 | 161 | // non length versions. Zero terminated, so should be the same 162 | memset(vstr, 0, strsize); 163 | memset(str2, 0, strsize); 164 | EXPECT_EQ(strcpy(vstr, str), vstr); 165 | EXPECT_EQ(strcpy(str2, vstr), str2); 166 | EXPECT_EQ(strncmp(str, str2, strsize), 0); 167 | 168 | } 169 | 170 | TEST_F(UtilsFixture, strncmpTest) 171 | { 172 | const int strsize = 10; 173 | CharVirtPtr vstr = VAllocFixture::valloc.alloc(strsize); 174 | CharVirtPtr vstr2 = VAllocFixture::valloc.alloc(strsize); 175 | char str[strsize] = "Howdy!", str2[strsize]; 176 | 177 | strncpy(vstr, str, strsize); 178 | strncpy(vstr2, str, strsize); 179 | EXPECT_EQ(strncmp(vstr, str, strsize), 0); 180 | EXPECT_EQ(strncmp(str, vstr, strsize), 0); 181 | EXPECT_EQ(strncmp(vstr, vstr2, strsize), 0); 182 | 183 | strcpy(str2, str); 184 | 185 | str2[1] = 'a'; vstr2[1] = 'a'; 186 | EXPECT_EQ(clampOne(strncmp(vstr, vstr2, strsize)), clampOne(strncmp(str, str2, strsize))); 187 | EXPECT_EQ(clampOne(strncmp(vstr2, vstr, strsize)), clampOne(strncmp(str2, str, strsize))); 188 | 189 | // non length versions. Zero terminated, so should be the same 190 | EXPECT_EQ(strncmp(vstr, vstr2, strsize), strcmp(vstr, vstr2)); 191 | EXPECT_EQ(strncmp(vstr2, vstr, strsize), strcmp(vstr2, vstr)); 192 | EXPECT_EQ(strncmp(vstr, str2, strsize), strcmp(vstr, str2)); 193 | EXPECT_EQ(strncmp(str2, vstr, strsize), strcmp(str2, vstr)); 194 | 195 | VPtr cvstr = vstr; 196 | EXPECT_EQ(strcmp(cvstr, vstr), 0); 197 | } 198 | -------------------------------------------------------------------------------- /benchmark/deployment.pri: -------------------------------------------------------------------------------- 1 | # This file was generated by an application wizard of Qt Creator. 2 | # The code below handles deployment to Android and Maemo, aswell as copying 3 | # of the application data to shadow build directories on desktop. 4 | # It is recommended not to modify this file, since newer versions of Qt Creator 5 | # may offer an updated version of it. 6 | 7 | defineTest(qtcAddDeployment) { 8 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 9 | item = item$${deploymentfolder} 10 | greaterThan(QT_MAJOR_VERSION, 4) { 11 | itemsources = $${item}.files 12 | } else { 13 | itemsources = $${item}.sources 14 | } 15 | $$itemsources = $$eval($${deploymentfolder}.source) 16 | itempath = $${item}.path 17 | $$itempath= $$eval($${deploymentfolder}.target) 18 | export($$itemsources) 19 | export($$itempath) 20 | DEPLOYMENT += $$item 21 | } 22 | 23 | MAINPROFILEPWD = $$PWD 24 | 25 | android-no-sdk { 26 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 27 | item = item$${deploymentfolder} 28 | itemfiles = $${item}.files 29 | $$itemfiles = $$eval($${deploymentfolder}.source) 30 | itempath = $${item}.path 31 | $$itempath = /data/user/qt/$$eval($${deploymentfolder}.target) 32 | export($$itemfiles) 33 | export($$itempath) 34 | INSTALLS += $$item 35 | } 36 | 37 | target.path = /data/user/qt 38 | 39 | export(target.path) 40 | INSTALLS += target 41 | } else:android { 42 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 43 | item = item$${deploymentfolder} 44 | itemfiles = $${item}.files 45 | $$itemfiles = $$eval($${deploymentfolder}.source) 46 | itempath = $${item}.path 47 | $$itempath = /assets/$$eval($${deploymentfolder}.target) 48 | export($$itemfiles) 49 | export($$itempath) 50 | INSTALLS += $$item 51 | } 52 | 53 | x86 { 54 | target.path = /libs/x86 55 | } else: armeabi-v7a { 56 | target.path = /libs/armeabi-v7a 57 | } else { 58 | target.path = /libs/armeabi 59 | } 60 | 61 | export(target.path) 62 | INSTALLS += target 63 | } else:win32 { 64 | copyCommand = 65 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 66 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 67 | source = $$replace(source, /, \\) 68 | sourcePathSegments = $$split(source, \\) 69 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target)/$$last(sourcePathSegments) 70 | target = $$replace(target, /, \\) 71 | target ~= s,\\\\\\.?\\\\,\\, 72 | !isEqual(source,$$target) { 73 | !isEmpty(copyCommand):copyCommand += && 74 | isEqual(QMAKE_DIR_SEP, \\) { 75 | copyCommand += $(COPY_DIR) \"$$source\" \"$$target\" 76 | } else { 77 | source = $$replace(source, \\\\, /) 78 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target) 79 | target = $$replace(target, \\\\, /) 80 | copyCommand += test -d \"$$target\" || mkdir -p \"$$target\" && cp -r \"$$source\" \"$$target\" 81 | } 82 | } 83 | } 84 | !isEmpty(copyCommand) { 85 | copyCommand = @echo Copying application data... && $$copyCommand 86 | copydeploymentfolders.commands = $$copyCommand 87 | first.depends = $(first) copydeploymentfolders 88 | export(first.depends) 89 | export(copydeploymentfolders.commands) 90 | QMAKE_EXTRA_TARGETS += first copydeploymentfolders 91 | } 92 | } else:ios { 93 | copyCommand = 94 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 95 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 96 | source = $$replace(source, \\\\, /) 97 | target = $CODESIGNING_FOLDER_PATH/$$eval($${deploymentfolder}.target) 98 | target = $$replace(target, \\\\, /) 99 | sourcePathSegments = $$split(source, /) 100 | targetFullPath = $$target/$$last(sourcePathSegments) 101 | targetFullPath ~= s,/\\.?/,/, 102 | !isEqual(source,$$targetFullPath) { 103 | !isEmpty(copyCommand):copyCommand += && 104 | copyCommand += mkdir -p \"$$target\" 105 | copyCommand += && cp -r \"$$source\" \"$$target\" 106 | } 107 | } 108 | !isEmpty(copyCommand) { 109 | copyCommand = echo Copying application data... && $$copyCommand 110 | !isEmpty(QMAKE_POST_LINK): QMAKE_POST_LINK += ";" 111 | QMAKE_POST_LINK += "$$copyCommand" 112 | export(QMAKE_POST_LINK) 113 | } 114 | } else:unix { 115 | maemo5 { 116 | desktopfile.files = $${TARGET}.desktop 117 | desktopfile.path = /usr/share/applications/hildon 118 | icon.files = $${TARGET}64.png 119 | icon.path = /usr/share/icons/hicolor/64x64/apps 120 | } else:!isEmpty(MEEGO_VERSION_MAJOR) { 121 | desktopfile.files = $${TARGET}_harmattan.desktop 122 | desktopfile.path = /usr/share/applications 123 | icon.files = $${TARGET}80.png 124 | icon.path = /usr/share/icons/hicolor/80x80/apps 125 | } else { # Assumed to be a Desktop Unix 126 | copyCommand = 127 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 128 | source = $$MAINPROFILEPWD/$$eval($${deploymentfolder}.source) 129 | source = $$replace(source, \\\\, /) 130 | macx { 131 | target = $$OUT_PWD/$${TARGET}.app/Contents/Resources/$$eval($${deploymentfolder}.target) 132 | } else { 133 | target = $$OUT_PWD/$$eval($${deploymentfolder}.target) 134 | } 135 | target = $$replace(target, \\\\, /) 136 | sourcePathSegments = $$split(source, /) 137 | targetFullPath = $$target/$$last(sourcePathSegments) 138 | targetFullPath ~= s,/\\.?/,/, 139 | !isEqual(source,$$targetFullPath) { 140 | !isEmpty(copyCommand):copyCommand += && 141 | copyCommand += $(MKDIR) \"$$target\" 142 | copyCommand += && $(COPY_DIR) \"$$source\" \"$$target\" 143 | } 144 | } 145 | !isEmpty(copyCommand) { 146 | copyCommand = @echo Copying application data... && $$copyCommand 147 | copydeploymentfolders.commands = $$copyCommand 148 | first.depends = $(first) copydeploymentfolders 149 | export(first.depends) 150 | export(copydeploymentfolders.commands) 151 | QMAKE_EXTRA_TARGETS += first copydeploymentfolders 152 | } 153 | } 154 | !isEmpty(target.path) { 155 | installPrefix = $${target.path} 156 | } else { 157 | installPrefix = /opt/$${TARGET} 158 | } 159 | for(deploymentfolder, DEPLOYMENTFOLDERS) { 160 | item = item$${deploymentfolder} 161 | itemfiles = $${item}.files 162 | $$itemfiles = $$eval($${deploymentfolder}.source) 163 | itempath = $${item}.path 164 | $$itempath = $${installPrefix}/$$eval($${deploymentfolder}.target) 165 | export($$itemfiles) 166 | export($$itempath) 167 | INSTALLS += $$item 168 | } 169 | 170 | !isEmpty(desktopfile.path) { 171 | export(icon.files) 172 | export(icon.path) 173 | export(desktopfile.files) 174 | export(desktopfile.path) 175 | INSTALLS += icon desktopfile 176 | } 177 | 178 | isEmpty(target.path) { 179 | target.path = $${installPrefix}/bin 180 | export(target.path) 181 | } 182 | INSTALLS += target 183 | } 184 | 185 | export (ICON) 186 | export (INSTALLS) 187 | export (DEPLOYMENT) 188 | export (LIBS) 189 | export (QMAKE_EXTRA_TARGETS) 190 | } 191 | 192 | -------------------------------------------------------------------------------- /virtmem/src/config/config.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_CONFIG_H 2 | #define VIRTMEM_CONFIG_H 3 | 4 | /** 5 | * @file 6 | * @brief This header file contains several variables that can be used to customize virtmem. 7 | */ 8 | 9 | #include 10 | 11 | #ifdef DOXYGEN 12 | // prevent double definitions for Doxygen 13 | #undef VIRTMEM_WRAP_CPOINTERS 14 | #undef VIRTMEM_VIRT_ADDRESS_OPERATOR 15 | #undef VIRTMEM_TRACE_STATS 16 | #undef VIRTMEM_CPP11 17 | #undef VIRTMEM_EXPLICIT 18 | #endif 19 | 20 | /** 21 | * @def VIRTMEM_WRAP_CPOINTERS 22 | * @brief If defined, enable wrapping of regular pointers inside virtmem::VPtr. 23 | * @sa virtmem::VPtr::wrap 24 | */ 25 | //#define VIRTMEM_WRAP_CPOINTERS 26 | 27 | /** 28 | * @def VIRTMEM_VIRT_ADDRESS_OPERATOR 29 | * @brief If defined, the "address of" operator (`&`) of VPtr will be overloaded to 30 | * return a virtual pointer that has its own address wrapped. 31 | * 32 | * This is useful to allow double pointers, for example: 33 | * @code 34 | * typedef virtmem::VPtr vptrType; 35 | * vptrType vptr; 36 | * virtmem::VPtr vptrptr = &vptr; 37 | * @endcode 38 | */ 39 | #define VIRTMEM_VIRT_ADDRESS_OPERATOR 40 | 41 | /** 42 | * @def VIRTMEM_TRACE_STATS 43 | * @brief If defined, several functions in the allocator will be defined that can be used to 44 | * access statistics such as memory usage and page swaps. 45 | * @see \ref statf "Statistics functions" 46 | */ 47 | //#define VIRTMEM_TRACE_STATS 48 | 49 | /** 50 | * @brief The default poolsize for allocators supporting a variable sized pool. 51 | * 52 | * This value is used for variable sized allocators, such as SDVAlloc and 53 | * SerialVAlloc. 54 | */ 55 | #define VIRTMEM_DEFAULT_POOLSIZE 1024l * 1024l 56 | 57 | /** 58 | * @def VIRTMEM_CPP11 59 | * @brief Enabled if current platform enables C++11 support (e.g. Teensyduino, Arduino >=1.6.6) 60 | */ 61 | #if __cplusplus > 199711L 62 | #define VIRTMEM_CPP11 63 | #endif 64 | 65 | /** 66 | * @def VIRTMEM_EXPLICIT 67 | * @brief Used for explicit conversion operators. 68 | * 69 | * Officially only C++11 and later support this, but this seems to work for not too 70 | * old gcc versions (>= 4.5) even if C++11 is not enabled. An empty definition will disable it. 71 | */ 72 | #define VIRTMEM_EXPLICIT explicit 73 | 74 | namespace virtmem { 75 | 76 | // Default virtual memory page settings 77 | // NOTE: Take care of sufficiently large int types when increasing these values 78 | 79 | #if defined(__MK20DX256__) || defined(__SAM3X8E__) // Teensy 3.1 / Arduino Due (>= 64 kB sram) 80 | struct DefaultAllocProperties 81 | { 82 | static const uint8_t smallPageCount = 4, smallPageSize = 64; 83 | static const uint8_t mediumPageCount = 4; 84 | static const uint16_t mediumPageSize = 256; 85 | static const uint8_t bigPageCount = 4; 86 | static const uint16_t bigPageSize = 1024 * 1; 87 | }; 88 | #elif defined(__MK20DX128__) // Teensy 3.0 (16 kB sram) 89 | struct DefaultAllocProperties 90 | { 91 | static const uint8_t smallPageCount = 4, smallPageSize = 32; 92 | static const uint8_t mediumPageCount = 4, mediumPageSize = 128; 93 | static const uint8_t bigPageCount = 4; 94 | static const uint16_t bigPageSize = 512 * 1; 95 | }; 96 | // Teensy LC / Arduino mega (8 kB sram) 97 | #elif defined(__MKL26Z64__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 98 | struct DefaultAllocProperties 99 | { 100 | static const uint8_t smallPageCount = 4, smallPageSize = 32; 101 | static const uint8_t mediumPageCount = 4, mediumPageSize = 128; 102 | static const uint8_t bigPageCount = 4; 103 | static const uint16_t bigPageSize = 512 * 1; 104 | }; 105 | // PC like platform 106 | #elif defined(__unix__) || defined(__UNIX__) || (defined(__APPLE__) && defined(__MACH__)) || defined(_WIN32) 107 | struct DefaultAllocProperties 108 | { 109 | static const uint8_t smallPageCount = 4, smallPageSize = 64; 110 | static const uint8_t mediumPageCount = 4; 111 | static const uint16_t mediumPageSize = 256; 112 | static const uint8_t bigPageCount = 4; 113 | static const uint16_t bigPageSize = 1024 * 32; 114 | }; 115 | #else 116 | // Small AVR like MCUs (e.g. Arduino Uno) or unknown platform. In the latter case these settings 117 | // are considered as a safe default. 118 | 119 | // Not a small AVR? 120 | #if !defined(__AVR_ATmega168__) && !defined(__AVR_ATmega328P__) && !defined(__AVR_ATmega32U4__) 121 | #warning "Unknown platform. You probably want to change virtual memory page settings." 122 | #endif 123 | 124 | struct DefaultAllocProperties 125 | { 126 | static const uint8_t smallPageCount = 2, smallPageSize = 16; 127 | static const uint8_t mediumPageCount = 1, mediumPageSize = 32; 128 | static const uint8_t bigPageCount = 1, bigPageSize = 128; 129 | }; 130 | 131 | #endif 132 | 133 | /** 134 | * @struct DefaultAllocProperties 135 | * @brief This struct contains default parameters for virtual memory pages. 136 | * 137 | * The fields in this struct define the amount- and size of the *small*, *medium* and *big* 138 | * memory pages for an allocator. The former two memory pages are only used for locking data. 139 | * In general it is best to make sure that they are big enough to contain any structs/classes 140 | * stored in virtual memory. The *big* memory pages are also used for data locking, but more importantly, they are used 141 | * as a cache for virtual memory access (see [basics](@ref basics)). 142 | * 143 | * Since all memory pages reside in regular RAM, changing their size and amount greatly 144 | * influences the RAM used by virtmem. Besides reducing the RAM usage by lowering the 145 | * amount and/or size of memory pages, it is also interesting to increase these numbers if you 146 | * have some spare RAM. In general, increasing the number of pages will enhance random access 147 | * times, whereas increasing the size of memory pages will reduce costly swapping and improve 148 | * sequential memory access. 149 | * 150 | * The default size and amount of memory pages is platform dependent, and can be changed in 151 | * config.h. Alternatively, page settings can be set by defining a customized structure 152 | * and passing this structure as a template parameter to an allocator. 153 | * 154 | * Example: 155 | * @code{.cpp} 156 | // This struct contains a customized set of memory page properties. 157 | // While the datatype of each member does not matter, all members must be static. 158 | struct AllocProperties 159 | { 160 | static const uint8_t smallPageCount = 4, smallPageSize = 64; 161 | static const uint8_t mediumPageCount = 4, mediumPageSize = 128; 162 | static const uint8_t bigPageCount = 4, 163 | static const uint16_t bigPageSize = 512; // note: uint16_t to contain larger numeric value 164 | }; 165 | 166 | // Create allocator with customized page properties 167 | SDVAllocP alloc; 168 | @endcode 169 | * 170 | * @sa @ref alloc_properties.ino example 171 | * 172 | * @var DefaultAllocProperties::smallPageCount 173 | * @brief The number of *small* pages. @hideinitializer 174 | * @var DefaultAllocProperties::mediumPageCount 175 | * @brief The number of *medium* pages. @hideinitializer 176 | * @var DefaultAllocProperties::bigPageCount 177 | * @brief The number of *big* pages. @hideinitializer 178 | * 179 | * @var DefaultAllocProperties::smallPageSize 180 | * @brief The size of a *small* page. @hideinitializer 181 | * @var DefaultAllocProperties::mediumPageSize 182 | * @brief The size of a *medium* page. @hideinitializer 183 | * @var DefaultAllocProperties::bigPageSize 184 | * @brief The size of a *big* page. @hideinitializer 185 | */ 186 | 187 | /** 188 | * @example alloc_properties.ino 189 | * This example shows the size and amount of memory pages of an allocator can be configured. 190 | */ 191 | 192 | } 193 | 194 | #ifdef DOXYGEN 195 | // Define macros so Doxygen can generate documentation for normally undefined macros 196 | #ifndef VIRTMEM_WRAP_CPOINTERS 197 | #define VIRTMEM_WRAP_CPOINTERS 198 | #endif 199 | 200 | #ifndef VIRTMEM_VIRT_ADDRESS_OPERATOR 201 | #define VIRTMEM_VIRT_ADDRESS_OPERATOR 202 | #endif 203 | 204 | #ifndef VIRTMEM_CPP11 205 | #define VIRTMEM_CPP11 206 | #endif 207 | 208 | #ifndef VIRTMEM_TRACE_STATS 209 | #define VIRTMEM_TRACE_STATS 210 | #endif 211 | #endif 212 | 213 | #endif // CONFIG_H 214 | -------------------------------------------------------------------------------- /virtmem/src/internal/base_alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_BASE_ALLOC_H 2 | #define VIRTMEM_BASE_ALLOC_H 3 | 4 | /** 5 | @file 6 | @brief Base virtual memory class header 7 | */ 8 | 9 | #define NVALGRIND 10 | 11 | #ifndef NVALGRIND 12 | # include 13 | #endif 14 | 15 | #include "config/config.h" 16 | 17 | #include 18 | 19 | namespace virtmem { 20 | 21 | typedef uint32_t VPtrNum; //!< Numeric type used to store raw virtual pointer addresses 22 | typedef uint32_t VPtrSize; //!< Numeric type used to store the size of a virtual memory block 23 | typedef uint16_t VirtPageSize; //!< Numeric type used to store the size of a virtual memory page 24 | 25 | /** 26 | * @brief Base class for virtual memory allocators. 27 | * 28 | * This class defines methods to (de)initialize the allocator (see \ref start() and \ref stop()). 29 | * In addition, this class can be used for 'raw' memory access. 30 | */ 31 | class BaseVAlloc 32 | { 33 | protected: 34 | // \cond HIDDEN_SYMBOLS 35 | #if defined(__x86_64__) || defined(_M_X64) 36 | typedef __uint128_t TAlign; 37 | #else 38 | typedef double TAlign; 39 | #endif 40 | 41 | private: 42 | enum 43 | { 44 | PAGE_MAX_CLEAN_SKIPS = 5, // if page is dirty: max tries for finding another clean page when swapping 45 | START_OFFSET = sizeof(TAlign), // don't start at zero so we can have NULL pointers 46 | BASE_INDEX = 1, // Special pointer to baseFreeList, not actually stored in file 47 | MIN_ALLOC_SIZE = 16 48 | }; 49 | 50 | union UMemHeader 51 | { 52 | struct 53 | { 54 | VPtrNum next; 55 | VPtrSize size; 56 | } s; 57 | 58 | TAlign alignDummy; 59 | }; 60 | 61 | protected: 62 | #ifndef NVALGRIND 63 | static const int valgrindPad = 12; 64 | #endif 65 | 66 | // \cond HIDDEN_SYMBOLS 67 | struct LockPage 68 | { 69 | VPtrNum start; 70 | VirtPageSize size; 71 | uint8_t *pool; 72 | uint8_t locks, cleanSkips; 73 | bool dirty; 74 | int8_t next; 75 | 76 | LockPage(void) : start(0), size(0), pool(0), locks(0), cleanSkips(0), dirty(false), next(-1) { } 77 | }; 78 | // \endcond 79 | 80 | private: 81 | struct PageInfo 82 | { 83 | LockPage *pages; 84 | VirtPageSize size; 85 | uint8_t count; 86 | int8_t freeIndex, lockedIndex; 87 | }; 88 | 89 | // Stuff configured from VAlloc 90 | VPtrSize poolSize; 91 | PageInfo smallPages, mediumPages, bigPages; 92 | 93 | UMemHeader baseFreeList; 94 | VPtrNum freePointer; 95 | VPtrNum poolFreePos; 96 | int8_t nextPageToSwap; 97 | 98 | #ifdef VIRTMEM_TRACE_STATS 99 | VPtrSize memUsed, maxMemUsed; 100 | uint32_t bigPageReads, bigPageWrites, bytesRead, bytesWritten; 101 | #endif 102 | 103 | void initPages(PageInfo *info, LockPage *pages, uint8_t *pool, uint8_t pcount, VirtPageSize psize); 104 | VPtrNum getMem(VPtrSize size); 105 | void syncBigPage(LockPage *page); 106 | void copyRawData(void *dest, VPtrNum p, VPtrSize size); 107 | void saveRawData(void *src, VPtrNum p, VPtrSize size); 108 | void *pullRawData(VPtrNum p, VPtrSize size, bool readonly, bool forcestart); 109 | void pushRawData(VPtrNum p, const void *d, VPtrSize size); 110 | const UMemHeader *getHeaderConst(VPtrNum p); 111 | void updateHeader(VPtrNum p, UMemHeader *h); 112 | int8_t findFreePage(PageInfo *pinfo, VPtrNum p, VPtrSize size, bool atstart); 113 | int8_t findUnusedLockedPage(PageInfo *pinfo); 114 | void syncLockedPage(LockPage *page); 115 | int8_t lockPage(PageInfo *pinfo, VPtrNum ptr, VirtPageSize size); 116 | int8_t freeLockedPage(PageInfo *pinfo, int8_t index); 117 | int8_t findLockedPage(PageInfo *pinfo, VPtrNum p); 118 | LockPage *findLockedPage(VPtrNum p); 119 | uint8_t getFreePages(const PageInfo *pinfo) const; 120 | uint8_t getUnlockedPages(const PageInfo *pinfo) const; 121 | 122 | protected: 123 | BaseVAlloc(void) : poolSize(0) { } 124 | 125 | // \cond HIDDEN_SYMBOLS 126 | void initSmallPages(LockPage *pages, uint8_t *pool, uint8_t pcount, VirtPageSize psize) { initPages(&smallPages, pages, pool, pcount, psize); } 127 | void initMediumPages(LockPage *pages, uint8_t *pool, uint8_t pcount, VirtPageSize psize) { initPages(&mediumPages, pages, pool, pcount, psize); } 128 | void initBigPages(LockPage *pages, uint8_t *pool, uint8_t pcount, VirtPageSize psize) { initPages(&bigPages, pages, pool, pcount, psize); } 129 | // \endcond 130 | 131 | void writeZeros(VPtrNum start, VPtrSize n); // NOTE: only call this in doStart() 132 | 133 | /** 134 | * @name Pure virtual functions 135 | * The following functions should be defined by derived allocator classes. 136 | * @{ 137 | */ 138 | virtual void doStart(void) = 0; 139 | virtual void doStop(void) = 0; 140 | virtual void doRead(void *data, VPtrSize offset, VPtrSize size) = 0; 141 | virtual void doWrite(const void *data, VPtrSize offset, VPtrSize size) = 0; 142 | //! @} 143 | 144 | public: 145 | void start(void); 146 | void stop(void); 147 | 148 | /** 149 | * @brief Sets the total size of the memory pool. 150 | * @param ps size of the memory pool. 151 | * @note The poolsize can also be set via the constructor of most allocators. 152 | * @note This function is unavailable for MultiSPIRAMVAllocP and StaticVAlloc. 153 | * @note This function should always called before \ref start(). 154 | */ 155 | void setPoolSize(VPtrSize ps) { poolSize = ps; } 156 | 157 | VPtrNum allocRaw(VPtrSize size); 158 | void freeRaw(VPtrNum ptr); 159 | 160 | void *read(VPtrNum p, VPtrSize size); 161 | void write(VPtrNum p, const void *d, VPtrSize size); 162 | void flush(void); 163 | void clearPages(void); 164 | uint8_t getFreeBigPages(void) const; 165 | uint8_t getUnlockedSmallPages(void) const { return getUnlockedPages(&smallPages); } //!< Returns amount of *small* pages which are not locked. 166 | uint8_t getUnlockedMediumPages(void) const { return getUnlockedPages(&mediumPages); } //!< Returns amount of *medium* pages which are not locked. 167 | uint8_t getUnlockedBigPages(void) const { return getUnlockedPages(&bigPages); } //!< Returns amount of *big* pages which are not locked. 168 | 169 | // \cond HIDDEN_SYMBOLS 170 | void *makeDataLock(VPtrNum ptr, VirtPageSize size, bool ro=false); 171 | void *makeFittingLock(VPtrNum ptr, VirtPageSize &size, bool ro=false); 172 | void releaseLock(VPtrNum ptr); 173 | // \endcond 174 | 175 | uint8_t getSmallPageCount(void) const { return smallPages.count; } //!< Returns total amount of *small* pages. 176 | uint8_t getMediumPageCount(void) const { return mediumPages.count; } //!< Returns total amount of *medium* pages. 177 | uint8_t getBigPageCount(void) const { return bigPages.count; } //!< Returns total amount of *big* pages. 178 | VirtPageSize getSmallPageSize(void) const { return smallPages.size; } //!< Returns the size of a *small* page. 179 | VirtPageSize getMediumPageSize(void) const { return mediumPages.size; } //!< Returns the size of a *medium* page. 180 | VirtPageSize getBigPageSize(void) const { return bigPages.size; } //!< Returns the size of a *big* page. 181 | 182 | /** 183 | * @brief Returns the size the memory pool. 184 | * @note Some memory is used for bookkeeping, therefore, the amount returned by this function 185 | * does *not* equate the amount that can be allocated. 186 | */ 187 | VPtrSize getPoolSize(void) const { return poolSize; } 188 | 189 | // \cond HIDDEN_SYMBOLS 190 | void printStats(void); 191 | // \endcond 192 | 193 | #ifdef VIRTMEM_TRACE_STATS 194 | /** 195 | * @anchor statf 196 | * @name Statistics functions. 197 | * The following functions are only available when VIRTMEM_TRACE_STATS is defined (in config.h). 198 | */ 199 | //@{ 200 | VPtrSize getMemUsed(void) const { return memUsed; } //!< Returns total memory used. 201 | VPtrSize getMaxMemUsed(void) const { return maxMemUsed; } //!< Returns the maximum memory used so far. 202 | uint32_t getBigPageReads(void) const { return bigPageReads; } //!< Returns the times *big* pages were read (swapped). 203 | uint32_t getBigPageWrites(void) const { return bigPageWrites; } //!< Returns the times *big* pages written (synchronized). 204 | uint32_t getBytesRead(void) const { return bytesRead; } //!< Returns the amount of bytes read as a result of page swaps. 205 | uint32_t getBytesWritten(void) const { return bytesWritten; } //!< Returns the amount of bytes written as a results of page swaps. 206 | void resetStats(void) { memUsed = maxMemUsed = 0; bigPageReads = bigPageWrites = bytesRead = bytesWritten = 0; } //!< Reset all statistics. Called by \ref start() 207 | //@} 208 | #endif 209 | }; 210 | 211 | } 212 | 213 | #endif // VIRTMEM_BASE_ALLOC_H 214 | -------------------------------------------------------------------------------- /virtmem/src/alloc/spiram_alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_SPIRAM_ALLOC_H 2 | #define VIRTMEM_SPIRAM_ALLOC_H 3 | 4 | /** 5 | * @file 6 | * @brief This file contains the SPI RAM virtual memory allocator 7 | */ 8 | 9 | #include 10 | #include "internal/alloc.h" 11 | 12 | #include 13 | 14 | namespace virtmem { 15 | 16 | /** 17 | * @brief Virtual memory allocator that uses SPI (serial) RAM (i.e. the 23LC series from Microchip) 18 | * as memory pool. 19 | * 20 | * This class uses an external SRAM chip as a memory pool. Interfacing occurs through the 21 | * [serialram library](https://github.com/rhelmus/serialram) and must be installed in order to use 22 | * this allocator. 23 | * 24 | * @tparam Properties Allocator properties, see DefaultAllocProperties 25 | * 26 | * @sa @ref bUsing and MultiSPIRAMVAllocP 27 | */ 28 | template 29 | class SPIRAMVAllocP : public VAlloc > 30 | { 31 | bool largeAddressing; 32 | uint8_t chipSelect; 33 | SerialRam::ESPISpeed SPISpeed; 34 | SerialRam serialRAM; 35 | 36 | void doStart(void) 37 | { 38 | serialRAM.begin(largeAddressing, chipSelect, SPISpeed); 39 | } 40 | 41 | void doStop(void) 42 | { 43 | serialRAM.end(); 44 | } 45 | 46 | void doRead(void *data, VPtrSize offset, VPtrSize size) 47 | { 48 | // const uint32_t t = micros(); 49 | serialRAM.read((char *)data, offset, size); 50 | // Serial.print("read: "); Serial.print(offset); Serial.print("/"); Serial.print(size); Serial.print("/"); Serial.println(micros() - t); 51 | } 52 | 53 | void doWrite(const void *data, VPtrSize offset, VPtrSize size) 54 | { 55 | // const uint32_t t = micros(); 56 | serialRAM.write((const char *)data, offset, size); 57 | // Serial.print("write: "); Serial.print(offset); Serial.print("/"); Serial.print(size); Serial.print("/"); Serial.println(micros() - t); 58 | } 59 | 60 | public: 61 | /** 62 | * @brief Constructs (but not initializes) the allocator. 63 | * @param ps Total amount of bytes of the memory pool (i.e. the size of the SRAM chip) 64 | * @param la `true` if large addressing (chipsize >= 1mbit) should be used 65 | * @param cs Chip select (CS) pin connected to the SRAM chip 66 | * @param s SPI speed (SerialRam::SPEED_FULL, SerialRam::SPEED_HALF or 67 | * SerialRam::SPEED_QUARTER) 68 | * @sa setSettings and setPoolSize 69 | */ 70 | SPIRAMVAllocP(VPtrSize ps, bool la, uint8_t cs, SerialRam::ESPISpeed s) : 71 | largeAddressing(la), chipSelect(cs), SPISpeed(s) { this->setPoolSize(ps); } 72 | /** 73 | * @brief Constructs (but not initializes) the allocator. 74 | * @param ps Total amount of bytes of the memory pool (i.e. the size of the SRAM chip) 75 | * @sa setSettings and setPoolSize 76 | */ 77 | SPIRAMVAllocP(VPtrSize ps) { this->setPoolSize(ps); } 78 | SPIRAMVAllocP(void) { } //!< Constructs (but not initializes) the allocator. 79 | ~SPIRAMVAllocP(void) { doStop(); } 80 | 81 | /** 82 | * @brief Configures the allocator. 83 | * 84 | * See SPIRAMVAlloc::SPIRAMVAlloc for a description of the parameters. 85 | * @note This function should only be called if the allocator is not initialized. 86 | */ 87 | void setSettings(bool la, uint8_t cs, SerialRam::ESPISpeed s) 88 | { 89 | largeAddressing = la; chipSelect = cs; SPISpeed = s; 90 | } 91 | }; 92 | 93 | typedef SPIRAMVAllocP<> SPIRAMVAlloc; //!< Shortcut to SPIRAMVAllocP with default template arguments 94 | 95 | /** 96 | * @brief This `struct` is used to configure each SRAM chip used by a MultiSPIRAMVAllocP 97 | * allocator. 98 | */ 99 | struct SPIRamConfig 100 | { 101 | bool largeAddressing; //!< Does this chip needs large addressing (`true` if size >= 1 Mbit) 102 | uint32_t size; //!< Amount of bytes this chip can hold 103 | uint8_t chipSelect; //!< Pin assigned as chip select (CS) for this chip 104 | SerialRam::ESPISpeed speed; //!< SPI speed to be used: SerialRam::SPEED_FULL, SerialRam::SPEED_HALF or SerialRam::SPEED_QUARTER 105 | }; 106 | 107 | /** 108 | * @brief Virtual allocator that uses multiple SRAM chips (i.e. 23LC series from Microchip) 109 | * as memory pool. 110 | * 111 | * This allocator is similar to SPIRAMVAlloc, but combines multiple SRAM chips as 112 | * one large memory pool. Interfacing occurs through the 113 | * [serialram library](https://github.com/rhelmus/serialram) and must be installed in order to use 114 | * this allocator. Every SRAM chip is configured by defining an SPIRamConfig array: 115 | * 116 | * @code{.cpp} 117 | * // configuration for two 23LC1024 chips, connected to CS pins 9 and 10. 118 | * virtmem::SPIRamConfig scfg[2] = { 119 | * { true, 1024 * 128, 9, SerialRam::SPEED_FULL }, 120 | * { true, 1024 * 128, 10, SerialRam::SPEED_FULL } }; 121 | * 122 | * virtmem::MultiSPIRAMVAllocP alloc; 123 | * @endcode 124 | * 125 | * @tparam SPIChips An array of SPIRamConfig that is used to configure each individual SRAM chip. 126 | * @tparam chipAmount Amount of SRAM chips to be used. 127 | * @tparam Properties Allocator properties, see DefaultAllocProperties 128 | * 129 | * @note The `serialram` library needs to be initialized (i.e. by calling CSerial::begin()) *before* 130 | * initializing this allocator. 131 | * @sa @ref bUsing, SPIRamConfig and SPIRAMVAllocP 132 | * 133 | */ 134 | template 135 | class MultiSPIRAMVAllocP : public VAlloc > 136 | { 137 | SerialRam serialRAM[chipAmount]; 138 | 139 | void doStart(void) 140 | { 141 | for (uint8_t i=0; i= startptr && offset < endptr) 159 | { 160 | const VPtrNum p = offset - startptr; // address relative in this chip 161 | const VPtrSize sz = private_utils::minimal(size, SPIChips[i].size - p); 162 | serialRAM[i].read((char *)data, p, sz); 163 | 164 | if (sz == size) 165 | break; 166 | 167 | size -= sz; 168 | data = (char *)data + sz; 169 | offset += sz; 170 | } 171 | startptr = endptr; 172 | } 173 | // Serial.print("read: "); Serial.print(size); Serial.print("/"); Serial.println(micros() - t); 174 | } 175 | 176 | void doWrite(const void *data, VPtrSize offset, VPtrSize size) 177 | { 178 | // const uint32_t t = micros(); 179 | VPtrNum startptr = 0; 180 | for (uint8_t i=0; i= startptr && offset < endptr) 184 | { 185 | const VPtrNum p = offset - startptr; // address relative in this chip 186 | const VPtrSize sz = private_utils::minimal(size, SPIChips[i].size - p); 187 | serialRAM[i].write((const char *)data, p, sz); 188 | 189 | if (sz == size) 190 | break; 191 | 192 | size -= sz; 193 | data = (const char *)data + sz; 194 | offset += sz; 195 | } 196 | startptr = endptr; 197 | } 198 | // Serial.print("write: "); Serial.print(size); Serial.print("/"); Serial.println(micros() - t); 199 | } 200 | 201 | using BaseVAlloc::setPoolSize; 202 | 203 | public: 204 | /** 205 | * Constructs the allocator. The pool size is automatically 206 | * deduced from the chip configurations. 207 | */ 208 | MultiSPIRAMVAllocP(void) 209 | { 210 | uint32_t ps = 0; 211 | for (uint8_t i=0; isetPoolSize(ps); 214 | } 215 | ~MultiSPIRAMVAllocP(void) { doStop(); } 216 | }; 217 | 218 | /** 219 | * @example spiram_simple.ino 220 | * This is a simple example sketch showing how to use the SPI RAM allocator. 221 | * 222 | * @example multispiram_simple.ino 223 | * This is a simple example sketch showing how to use the multi SPI RAM allocator. 224 | */ 225 | 226 | } 227 | 228 | #endif // VIRTMEM_SPIRAM_ALLOC_H 229 | -------------------------------------------------------------------------------- /virtmem/src/internal/vptr_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_VPTR_UTILS_H 2 | #define VIRTMEM_VPTR_UTILS_H 3 | 4 | /** 5 | * @file 6 | * @brief This file contains several utilities for virtual pointers. 7 | */ 8 | 9 | #include "config/config.h" 10 | #include "vptr.h" 11 | 12 | #include 13 | 14 | namespace virtmem { 15 | 16 | // Generic NULL type, partially based on https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/nullptr#Solution_and_Sample_Code 17 | /** @class NILL_t 18 | * @brief Generalized `NULL` pointer class 19 | * 20 | * This class can be used to assign a zero (`NULL`) value to both virtual and regular 21 | * pointers. This is useful, for instance, to write generic code where the type of a pointer is 22 | * unknown. The \ref virtmem namespace contains a global instance of the NILL_t class: [NILL](@ref virtmem::NILL). 23 | * @note On platforms supporting C++11 NILL_t is simply a `typedef` to [nullptr_t](http://en.cppreference.com/w/cpp/types/nullptr_t) 24 | * @sa [NILL](@ref virtmem::NILL) 25 | */ 26 | 27 | #ifndef VIRTMEM_CPP11 28 | class NILL_t 29 | { 30 | void operator&() const; // prevent taking address 31 | 32 | public: 33 | template inline operator T*(void) const { return 0; } 34 | template inline operator M C::*(void) const { return 0; } 35 | template inline operator VPtr(void) const { return VPtr(); } 36 | inline operator BaseVPtr(void) const { return BaseVPtr(); } 37 | template inline operator typename VPtr::ValueWrapper(void) const { return VPtr::ValueWrapper(0); } 38 | }; 39 | #else 40 | typedef nullptr_t NILL_t; 41 | #endif 42 | 43 | extern const NILL_t NILL; 44 | 45 | /** 46 | * @var NILL 47 | * @brief Global instance of a NILL_t class 48 | * 49 | * This variable can be used to assign a `NULL` pointer to both virtual and regular pointers. 50 | * Example: 51 | * @code 52 | * using namespace virtmem; 53 | * char *a = NILL; 54 | * VPtr b = NILL; 55 | * BaseVPtr c = NILL; 56 | * @endcode 57 | * @sa NILL_t 58 | */ 59 | 60 | /** 61 | * @brief Creates a lock to some virtual data 62 | * @tparam TV Type of virtual pointer that points to data 63 | * 64 | * This class is used to create and release locks to virtual data. The use of locks 65 | * allows more efficient data handling and compatibility with code only accepting regular 66 | * pointers. For more information see \ref aLocking. 67 | * 68 | * The class uses a RAII (resource acquisition is initialization) approach: after a 69 | * lock has been acquired (usually during construction), it will be automatically released 70 | * as soon as the class variable goes out of scope. 71 | * 72 | * [Wrapped regular pointers](@ref aWrapping) are supported: if a given virtual pointer wraps a 73 | * regular pointer, this class will not attempt to make or release a lock and 74 | * operator *(void) will simply return the wrapped pointer. 75 | */ 76 | template class VPtrLock 77 | { 78 | typedef typename TV::TPtr Ptr; 79 | 80 | TV virtPtr; 81 | Ptr data; 82 | VirtPageSize lockSize; 83 | bool readOnly; 84 | 85 | public: 86 | /** 87 | * @brief Constructs a virtual data lock class and creates a lock to the given data. 88 | * @param v A \ref VPtr "virtual pointer" to the data to be locked. 89 | * @param s Amount of bytes to lock. **Note**: the actual locked size may be smaller. 90 | * @param ro Whether locking should read-only (`true`) or not (`false`). If `ro` is 91 | * `false` (default), the locked data will always be synchronized after unlocking (even if unchanged). 92 | * Therefore, if no changes in data are expected, it is more efficient to set `ro` to `true`. 93 | * @sa getLockSize 94 | */ 95 | VPtrLock(const TV &v, VirtPageSize s, bool ro=false) : 96 | virtPtr(v), lockSize(s), readOnly(ro) { lock(); } 97 | /** 98 | * @brief Default constructor. No locks are created. 99 | * 100 | * The \ref lock(const TV &v, VirtPageSize s, bool ro) function should be used 101 | * to create a lock if this constructor is used. 102 | */ 103 | VPtrLock(void) : data(0), lockSize(0), readOnly(false) { } 104 | ~VPtrLock(void) { unlock(); } //!< Unlocks data if locked. 105 | VPtrLock(const VPtrLock &other) : 106 | virtPtr(other.virtPtr), lockSize(other.lockSize), 107 | readOnly(other.readOnly) { lock(); } //!< Copy constructor, adds extra lock to data 108 | 109 | /** 110 | * @brief Recreates a virtual data lock after \ref unlock was called. 111 | * @note This function will re-use the parameters for locking set by \ref VPtrLock(const TV &v, VirtPageSize s, bool ro) 112 | * or \ref lock(const TV &v, VirtPageSize s, bool ro). 113 | */ 114 | void lock(void) 115 | { 116 | #ifdef VIRTMEM_WRAP_CPOINTERS 117 | if (virtPtr.isWrapped()) 118 | data = virtPtr.unwrap(); 119 | else 120 | #endif 121 | data = static_cast(TV::getAlloc()->makeFittingLock(virtPtr.ptr, lockSize, readOnly)); 122 | } 123 | 124 | /** 125 | * @brief Locks data. Parameters are described \ref VPtrLock(const TV &v, VirtPageSize s, bool ro) "here". 126 | */ 127 | void lock(const TV &v, VirtPageSize s, bool ro=false) 128 | { 129 | virtPtr = v; lockSize = s; readOnly = ro; 130 | lock(); 131 | } 132 | 133 | /** 134 | * @brief Unlocks data (if locked). Automatically called during destruction. 135 | */ 136 | void unlock(void) 137 | { 138 | #ifdef VIRTMEM_WRAP_CPOINTERS 139 | if (!virtPtr.isWrapped() && data) 140 | #else 141 | if (data) 142 | #endif 143 | { 144 | TV::getAlloc()->releaseLock(virtPtr.ptr); 145 | data = 0; 146 | } 147 | } 148 | 149 | Ptr operator *(void) { return data; } //!< Provides access to the data. 150 | /** 151 | * @brief Returns the actual size locked. 152 | * 153 | * The **actual** size that was locked may be smaller then what was requested. 154 | * If the requested size was too large, for instance larger then the largest memory page size, 155 | * the locked size will be adjusted accordingly. For this reason, it is *very important* to 156 | * use this function after a lock has been created. 157 | */ 158 | VirtPageSize getLockSize(void) const { return lockSize; } 159 | }; 160 | 161 | /** 162 | * @example locking.ino 163 | * This example shows how to lock virtual data within regular RAM. 164 | */ 165 | 166 | // Shortcut 167 | /** 168 | * @brief Creates a virtual lock (shortcut) 169 | * 170 | * This function is a shortcut for making a virtual lock: unlike using VPtrLock, 171 | * you don't need to specify the virtual pointer type as a template parameter. The 172 | * function parameters are the same as VPtrLock::VPtrLock. 173 | * @sa VPtr and @ref aLocking 174 | */ 175 | template VPtrLock makeVirtPtrLock(const T &w, VirtPageSize s, bool ro=false) 176 | { return VPtrLock(w, s, ro); } 177 | 178 | namespace private_utils { 179 | // Ugly hack from http://stackoverflow.com/a/12141673 180 | // a null pointer of T is used to get the offset of m. The char & is to avoid any dereference operator overloads and the 181 | // char * subtraction to get the actual offset. 182 | template ptrdiff_t getMembrOffset(const M C::*m) 183 | { return ((char *)&((char &)((C *)(0)->*m)) - (char *)(0)); } 184 | } 185 | 186 | /** 187 | * @brief Obtains a virtual pointer to a data member that is stored in virtual memory. 188 | * 189 | * @tparam C Type of virtual data (e.g. the structure) 190 | * @tparam M Type of the data member 191 | * @tparam A Type of the virtual memory allocator 192 | * @param c The virtual pointer of the structure (or class) to which the data member belongs 193 | * @param m A pointer to the member (e.g. &MyStruct::x) 194 | * @sa [section about virtual pointers to data members in manual](@ref aPointStructMem) 195 | */ 196 | template inline VPtr getMembrPtr(const VPtr &c, const M C::*m) 197 | { VPtr ret; ret.setRawNum(c.getRawNum() + private_utils::getMembrOffset(m)); return ret; } 198 | 199 | /** 200 | * @brief Obtains a virtual pointer to a nested data member that is stored in virtual memory. 201 | * 202 | * @tparam C Type of virtual data (e.g. the structure) 203 | * @tparam M Type of the data member 204 | * @tparam NC Type of the nested structure 205 | * @tparam NM Type of nested data member 206 | * @tparam A Type of the virtual memory allocator 207 | * @param c The virtual pointer of the structure (or class) to which the data member belongs 208 | * @param m A pointer to the nested structure/class (e.g. &MyStruct::MyOtherStruct) 209 | * @param nm A pointer to the nested data member (e.g. &MyStruct::MyOtherStruct::y) 210 | * @sa [section about virtual pointers to data members in manual](@ref aPointStructMem) 211 | */ 212 | template inline VPtr getMembrPtr(const VPtr &c, const M C::*m, const NM NC::*nm) 213 | { VPtr ret = static_cast >(static_cast >(getMembrPtr(c, m)) + private_utils::getMembrOffset(nm)); return ret; } 214 | 215 | } 216 | 217 | #include "vptr_utils.hpp" 218 | 219 | #endif // VIRTMEM_VPTR_UTILS_H 220 | -------------------------------------------------------------------------------- /virtmem/src/internal/base_vptr.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_BASE_VPTR_H 2 | #define VIRTMEM_BASE_VPTR_H 3 | 4 | /** 5 | * @file 6 | * @brief This file contains the virtual pointer base class 7 | */ 8 | 9 | #include "config/config.h" 10 | #include "internal/alloc.h" 11 | #include "utils.h" 12 | 13 | #include 14 | 15 | namespace virtmem { 16 | 17 | #ifndef VIRTMEM_CPP11 18 | class NILL_t; 19 | #else 20 | typedef nullptr_t NILL_t; 21 | #endif 22 | 23 | namespace private_utils { 24 | 25 | template struct Conditional 26 | { 27 | typedef T1 type; 28 | }; 29 | 30 | template struct Conditional 31 | { 32 | typedef T2 type; 33 | }; 34 | 35 | } 36 | 37 | template class VPtr; 38 | 39 | /** 40 | * @brief This is the base class of VPtr and can be used for typeless pointers. 41 | * 42 | * This base class contains all the functionality of virtual pointers which do not depend 43 | * on any templated code to reduce code size. This class is designed in such a way that it can be 44 | * treated mostly as 'plain old data' (POD) and can therefore be used in an `union` as well. 45 | * 46 | * This class can be used for 'typeless' virtual pointers, similar as void* is used 47 | * for 'regular' typeless pointers. 48 | * @sa @ref aTypeless 49 | */ 50 | 51 | class BaseVPtr 52 | { 53 | struct Null { }; 54 | 55 | // Safe bool idiom from boost::function 56 | struct Dummy { void nonNull(void) { } }; 57 | typedef void (Dummy::*TSafeBool)(void); 58 | 59 | template friend class VPtr; 60 | template friend class VPtrLock; 61 | 62 | public: 63 | /** 64 | * @brief Platform dependent numeric type to store raw (virtual/regular) pointer addresses 65 | */ 66 | #ifdef VIRTMEM_WRAP_CPOINTERS 67 | #if defined(__x86_64__) || defined(_M_X64) 68 | typedef __uint128_t PtrNum; 69 | #elif defined(__i386) || defined(_M_IX86) 70 | typedef uint64_t PtrNum; 71 | #else 72 | typedef private_utils::Conditional<(sizeof(intptr_t) > sizeof(VPtrNum)), intptr_t, VPtrNum>::type PtrNum; 73 | #endif 74 | #else 75 | typedef VPtrNum PtrNum; 76 | #endif 77 | 78 | private: 79 | #ifdef VIRTMEM_WRAP_CPOINTERS 80 | enum { WRAP_BIT = sizeof(PtrNum) * 8 - 1 }; // Last bit is used to check if wrapping real pointer 81 | #endif 82 | 83 | protected: 84 | // UNDONE 85 | // For debug 86 | #if defined(VIRTMEM_WRAP_CPOINTERS) && (defined(__x86_64__) || defined(_M_X64)) 87 | union 88 | { 89 | PtrNum ptr; 90 | struct 91 | { 92 | VPtrNum addr, wrapped; 93 | } ugh; 94 | }; 95 | #else 96 | PtrNum ptr; //!< Numeric representation of this virtual pointer 97 | #endif 98 | 99 | // @cond HIDDEN_SYMBOLS 100 | #ifdef VIRTMEM_WRAP_CPOINTERS 101 | // Return 'real' address of pointer, ie without wrapping bit 102 | // static so that ValueWrapper can use it as well 103 | static intptr_t getPtrNum(PtrNum p) { return p & ~((PtrNum)1 << WRAP_BIT); } 104 | static void *unwrap(PtrNum p) { ASSERT(isWrapped(p)); return reinterpret_cast(getPtrNum(p)); } 105 | #else 106 | static intptr_t getPtrNum(PtrNum p) { return p; } 107 | #endif 108 | intptr_t getPtrNum(void) const { return getPtrNum(ptr); } // Shortcut 109 | // @endcond 110 | 111 | public: 112 | #ifdef VIRTMEM_CPP11 113 | // Can only use these constructors on C++11, otherwise vptrs cannot be used with unions 114 | BaseVPtr(void) = default; // Must have this to remain union compatible 115 | BaseVPtr(NILL_t) : ptr(0) { } //!< Construct from NILL/nullptr (C++11 only) 116 | #endif 117 | 118 | #ifdef VIRTMEM_WRAP_CPOINTERS 119 | /** 120 | * @brief Returns raw address of regular pointer wrapped by a virtual pointer. 121 | * @param p Numeric value of the virtual pointer 122 | * @sa getRawNum 123 | * @note \ref VIRTMEM_WRAP_CPOINTERS needs to be defined (e.g. in config.h) to enable this function. 124 | */ 125 | static PtrNum getWrapped(PtrNum p) { return p | ((PtrNum)1 << WRAP_BIT); } 126 | /** 127 | * @brief Returns whether a virtual pointer has wrapped a regular pointer. 128 | * @param p Numeric value of the virtual pointer 129 | * @sa getRawNum 130 | * @note \ref VIRTMEM_WRAP_CPOINTERS needs to be defined (e.g. in config.h) to enable this function. 131 | */ 132 | static bool isWrapped(PtrNum p) { return p & ((PtrNum)1 << WRAP_BIT); } 133 | /** 134 | * @brief Returns whether a virtual pointer has wrapped a regular pointer (non static version). 135 | * @sa getRawNum 136 | * @note \ref VIRTMEM_WRAP_CPOINTERS needs to be defined (e.g. in config.h) to enable this function. 137 | */ 138 | bool isWrapped(void) const { return isWrapped(ptr); } 139 | #endif 140 | 141 | /** 142 | * @brief Returns a numeric representation of this virtual pointer. 143 | * 144 | * This function can be used to convert virtual pointers to numeric values. 145 | * @note The value returned by this function depends whether this virtual pointer wraps a 146 | * regular pointer. If this is the case, the returned value contains the raw pointer address and 147 | * a flag to indicate that this is not a virtual pointer. In this case getWrapped can be used 148 | * to obtain the pointer address. If this virtual pointer does *not* wrap a regular pointer, 149 | * the value returned by this function equals a virtual pointer address that can be 150 | * used directly by an allocator for raw memory access. 151 | * @sa getWrapped, isWrapped 152 | */ 153 | PtrNum getRawNum(void) const { return ptr; } 154 | /** 155 | * @brief Sets a virtual pointer from a numeric value. 156 | * @param p The raw numeric representation of a virtual pointer. 157 | * @sa getRawNum 158 | */ 159 | void setRawNum(PtrNum p) { ptr = p; } 160 | 161 | #ifdef VIRTMEM_WRAP_CPOINTERS 162 | /** 163 | * @brief Wraps a regular pointer 164 | * @sa VPtr::wrap 165 | * @note \ref VIRTMEM_WRAP_CPOINTERS needs to be defined (e.g. in config.h) to enable this function. 166 | */ 167 | static BaseVPtr wrap(const void *p) 168 | { 169 | BaseVPtr ret; 170 | ret.ptr = getWrapped(reinterpret_cast(p)); 171 | return ret; 172 | } 173 | void *unwrap(const BaseVPtr &p) { return unwrap(p); } //!< @copydoc VPtr::unwrap 174 | void *unwrap(void) { return unwrap(ptr); } //!< @copydoc VPtr::unwrap(void) 175 | const void *unwrap(void) const { return unwrap(ptr); } //!< @copydoc VPtr::unwrap(void) const 176 | #endif 177 | 178 | // HACK: this allows constructing VPtr objects from BaseVPtr variables, similar to 179 | // initializing non void pointers with a void pointer 180 | // Note that we could have used a copy constructor in VPtr instead, but this would make the latter 181 | // class non-POD 182 | //! Conversion operator to VPtr types. 183 | template VIRTMEM_EXPLICIT operator VPtr(void) const { VPtr ret; ret.ptr = ptr; return ret; } 184 | 185 | // allow checking with NULL 186 | /** 187 | * @name Pointer validity checking operators. 188 | * The following operators can be used to see whether a virtual pointer is valid 189 | * (i.e. is not `NULL`). Overloads exist to support checking for `0`, `NULL` and \ref NILL. 190 | * The operators also work for wrapped regular pointers. 191 | * @{ 192 | */ 193 | #ifndef VIRTMEM_CPP11 194 | inline bool operator==(const Null *) const { return getPtrNum() == 0; } 195 | friend inline bool operator==(const Null *, const BaseVPtr &pw) { return pw.getPtrNum() == 0; } 196 | inline bool operator!=(const Null *) const { return getPtrNum() != 0; } 197 | friend inline bool operator!=(const Null *, const BaseVPtr &pw) { return pw.getPtrNum() != 0; } 198 | #endif 199 | inline bool operator==(const NILL_t &) const { return getPtrNum() == 0; } 200 | friend inline bool operator==(const NILL_t &, const BaseVPtr &pw) { return pw.getPtrNum() == 0; } 201 | inline bool operator!=(const NILL_t &) const { return getPtrNum() != 0; } 202 | friend inline bool operator!=(const NILL_t &, const BaseVPtr &pw) { return pw.getPtrNum() != 0; } 203 | //! Allows `if (myvirtptr) ...` expressions. 204 | inline operator TSafeBool (void) const { return getPtrNum() == 0 ? 0 : &Dummy::nonNull; } 205 | // @} 206 | 207 | /** 208 | * @name Pointer comparison operators. 209 | * The following operators are used for comparing two virtual pointers. Comparing two wrapped 210 | * regulared pointers is supported. Comparing wrapped and non wrapped pointers is undefined. 211 | * @{ 212 | */ 213 | // UNDONE: _int128_t comparison sometimes fails??? Perhaps only check for ptrnums/iswrapped when on x86-64? Test this! 214 | #if (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)) && defined(VIRTMEM_WRAP_CPOINTERS) 215 | inline bool operator==(const BaseVPtr &pb) const { return getPtrNum() == pb.getPtrNum() && isWrapped() == pb.isWrapped(); } 216 | inline bool operator!=(const BaseVPtr &pb) const { return getPtrNum() != pb.getPtrNum() && isWrapped() == pb.isWrapped(); } 217 | inline bool operator<(const BaseVPtr &pb) const { return getPtrNum() < pb.getPtrNum() && isWrapped() == pb.isWrapped(); } 218 | inline bool operator<=(const BaseVPtr &pb) const { return getPtrNum() <= pb.getPtrNum() && isWrapped() == pb.isWrapped(); } 219 | inline bool operator>=(const BaseVPtr &pb) const { return getPtrNum() >= pb.getPtrNum() && isWrapped() == pb.isWrapped(); } 220 | inline bool operator>(const BaseVPtr &pb) const { return getPtrNum() > pb.getPtrNum() && isWrapped() == pb.isWrapped(); } 221 | // @} 222 | #else 223 | inline bool operator==(const BaseVPtr &pb) const { return ptr == pb.ptr; } 224 | inline bool operator!=(const BaseVPtr &pb) const { return ptr != pb.ptr; } 225 | inline bool operator<(const BaseVPtr &pb) const { return ptr < pb.ptr; } 226 | inline bool operator<=(const BaseVPtr &pb) const { return ptr <= pb.ptr; } 227 | inline bool operator>=(const BaseVPtr &pb) const { return ptr >= pb.ptr; } 228 | inline bool operator>(const BaseVPtr &pb) const { return ptr > pb.ptr; } 229 | #endif 230 | 231 | }; 232 | 233 | } 234 | 235 | 236 | #endif // VIRTMEM_BASE_VPTR_H 237 | -------------------------------------------------------------------------------- /virtmem/src/alloc/serial_alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_SERIAL_ALLOC_H 2 | #define VIRTMEM_SERIAL_ALLOC_H 3 | 4 | /** 5 | * @file 6 | * @brief This file contains the serial virtual memory allocator 7 | */ 8 | 9 | #include 10 | #include "internal/alloc.h" 11 | #include "internal/serial_utils.h" 12 | 13 | namespace virtmem { 14 | 15 | /** 16 | * @brief Virtual memory allocator that uses external memory via a serial connection as 17 | * memory pool. 18 | * 19 | * This class utilizes RAM from an external device (e.g. a PC or ARM board) that is connected 20 | * with a serial port. This allocator is particularly useful when a permanant serial connection 21 | * with such a device exists, for instance on an Arduino Yun or Udoo ARM board. Since this 22 | * allocator requires no extra hardware (besides a serial connection), it is easy to test 23 | * and a good starting point and generally useful for debugging. The device that provides 24 | * the RAM (the _RAM host_) should run a script (`serial_host.py`) that is responsible for 25 | * communicating with `virtmem` and memory management. 26 | * 27 | * __Choosing a serial port__ 28 | * 29 | * By default, the allocator uses the default serial port (i.e. the `Serial` class). In this case, 30 | * the @ref SerialVAlloc shortcut can also be used: 31 | * @code{.cpp} 32 | * SerialVAllocP<> valloc; 33 | * SerialVAlloc valloc2; // same as above, both use the default serial port (Serial) 34 | * @endcode 35 | * If another serial port is desired, both the _type_ and (a pointer to the) _variable_ must 36 | * be specified: 37 | * @code{.cpp} 38 | * // Uses Serial1 with a default pool size and baudrate 39 | * SerialVAllocP valloc(VIRTMEM_DEFAULT_POOLSIZE, 115200, &Serial1); 40 | * @endcode 41 | * The `typeof` expression used in the above example may raise some questions. We need to know 42 | * the type of the serial class to be used (in this case the class that defines Serial1). Since 43 | * this not well standardized, we simply use the `typeof` expression to retrieve it. Note that this 44 | * is an extension of `gcc` (the compiler used by Arduino and many other platforms), and that 45 | * `decltype` could also be used on C++11 systems. Note that this approach is quite generic, 46 | * and as long as a class defines similar functions as the `SerialX` classes, other types 47 | * such as `SoftwareSerial` can also be used: 48 | * @code{.cpp} 49 | * SoftwareSerial sserial(10, 11); 50 | * SerialVAllocP valloc(SERIALRAM_POOLSIZE, 115200, &sserial); 51 | * @endcode 52 | * 53 | * __Communication with the RAM host__ 54 | * 55 | * A special [Python](https://www.python.org/) script needs to be run on the RAM host 56 | * (PC, ARM board etc.) that communicates with the Arduino code. This script allocates a 57 | * buffer that is sufficient in size to act as RAM for the code running `virtmem`. Both this 58 | * script and SerialVAlloc::start will block and wait until a connection is established between the two. 59 | * 60 | * The serial script is stored in `virtmem/extras/serial_host.py`, and requires Python 3 as well 61 | * as the [pyserial module](https://pythonhosted.org/pyserial/). 62 | * 63 | * Running the script with the `-h` (or `--help`) option will display the available 64 | * commandline parameters for configuration: 65 | * @verbatim 66 | usage: serial_host.py [-h] [-p PORT] [-b BAUD] [-l PASSDEV] [-r PASSBAUD] 67 | 68 | optional arguments: 69 | -h, --help show this help message and exit 70 | -p PORT, --port PORT serial device connected to the arduino. Default: 71 | COM3 (Windows) or /dev/ttyACM0 (other OS) 72 | -b BAUD, --baud BAUD Serial baudrate. Default: 115200 73 | -l PASSDEV, --pass PASSDEV 74 | serial pass through device 75 | -r PASSBAUD, --passbaud PASSBAUD 76 | baud rate of serial port pass through device. Default: 77 | 115200 78 | @endverbatim 79 | * The port (`-p` or `--port` option) should be set to the serial port connected 80 | * to the MCU running `virtmem` (e.g. /dev/ttyACM0, COM3 etc). The baudrate 81 | * (`-b` or `--baud` option) should match the configured baudrate of the allocator 82 | * (see SerialVAlloc::SerialVAllocP and SerialVAlloc::setBaudRate). 83 | * 84 | * The `serial_host.py` script allows to act as a serial bridge and can 'pass through' all 85 | * communication to and from the MCU to another (virtual) serial port. By setting up a 86 | * 'virtual serial port' (e.g. via [socat](http://www.dest-unreach.org/socat) or 87 | * [com0com](http://com0com.sourceforge.net)) and connecting `serial_host.py` to it, 88 | * other software is still able to communicate with your MCU. The (virtual) serial port 89 | * and its baudrate is set by the `-l` (or `--pass`) and `-r` (or `--passbaud`) options, 90 | * respectively. 91 | * 92 | * Some examples: 93 | * @code{.py} 94 | * serial_host.py # uses default serial port and baudrate 95 | * python serial_host.py -p COM2 # uses COM2 (Windows) as serial port, with default baudrate 96 | * serial_host.py -p /dev/ttyACM2 -b 9600 # uses /dev/ttyACM2 (Linux) as serial port, with 9600 baudrate 97 | * serial_host.py -l /dev/pts/1 # use default serial settings and pass all traffic through /dev/pts/1 98 | * @endcode 99 | * 100 | * Once the script has started it will keep monitoring the serial port until exited manually 101 | * (e.g. by pressing ctrl+C). Sending text can be done by simply writing the text and pressing 102 | * enter. 103 | * 104 | * __Sharing serial ports with other code__ 105 | * 106 | * Sometimes it may be desired to use the serial port used by `SerialVAllocP` for other purposes. 107 | * Even when a port is 'shared', simply writing to it is no problem and dealt with by `serial_host.py`. 108 | * For instance, when `Serial.print()` is called (and Serial is bound to SerialVAllocP), the text 109 | * is simply printed to the terminal running `serial_host.py`. 110 | * 111 | * Dealing with user input, however, is more tricky. Any text that is typed in the terminal running 112 | * `serial_host.py` is still sent to the MCU running `virtmem`. To retrieve it, the 113 | * SerialVAllocP::input class must be used: 114 | * @code{.cpp} 115 | * SerialVAlloc valloc; 116 | * // ... 117 | * if (valloc.input.available()) 118 | * { 119 | * char c = valloc.input.read(); 120 | * // ... 121 | * } 122 | * @endcode 123 | * Note that in the above example it is more efficient to use the [availableAtLeast function](@ref virtmem::serram_utils::SerialInput::availableAtLeast). 124 | * For more information see the serram_utils::SerialInput class. 125 | * 126 | * @tparam IOStream The type of serial class to use for communication. Default is the type of 127 | * the Serial class. 128 | * @tparam Properties Allocator properties, see DefaultAllocProperties 129 | * 130 | * @sa @ref bUsing, SerialVAlloc 131 | */ 132 | template 133 | class SerialVAllocP : public VAlloc > 134 | { 135 | uint32_t baudRate; 136 | IOStream *stream; 137 | 138 | void doStart(void) 139 | { 140 | serram_utils::init(stream, baudRate, this->getPoolSize()); 141 | } 142 | 143 | void doStop(void) { } 144 | 145 | void doRead(void *data, VPtrSize offset, VPtrSize size) 146 | { 147 | // uint32_t t = micros(); 148 | serram_utils::sendReadCommand(stream, serram_utils::CMD_READ); 149 | serram_utils::writeUInt32(stream, offset); 150 | serram_utils::writeUInt32(stream, size); 151 | Serial.flush(); 152 | serram_utils::readBlock(stream, (char *)data, size); 153 | // Serial.print("read: "); Serial.print(size); Serial.print("/"); Serial.println(micros() - t); 154 | } 155 | 156 | void doWrite(const void *data, VPtrSize offset, VPtrSize size) 157 | { 158 | // const uint32_t t = micros(); 159 | serram_utils::sendWriteCommand(stream, serram_utils::CMD_WRITE); 160 | serram_utils::writeUInt32(stream, offset); 161 | serram_utils::writeUInt32(stream, size); 162 | serram_utils::writeBlock(stream, (const uint8_t *)data, size); 163 | // Serial.print("write: "); Serial.print(size); Serial.print("/"); Serial.println(micros() - t); 164 | } 165 | 166 | public: 167 | /** 168 | * @brief Handles input of shared serial connections. 169 | * @sa serram_utils::SerialInput, SerialVAllocP 170 | */ 171 | serram_utils::SerialInput input; 172 | 173 | /** 174 | * @brief Constructs (but not initializes) the serial allocator. 175 | * @param ps The size of the virtual memory pool. 176 | * @param baud The baudrate. 177 | * @param s The instance variable of the serial class that should be used (e.g. Serial, Serial1) 178 | * @sa setBaudRate and setPoolSize 179 | */ 180 | SerialVAllocP(VPtrSize ps=VIRTMEM_DEFAULT_POOLSIZE, uint32_t baud=115200, IOStream *s=&Serial) : 181 | baudRate(baud), stream(s), input(stream) { this->setPoolSize(ps); } 182 | 183 | // only works before start() is called 184 | /** 185 | * @brief Sets the baud rate. 186 | * @param baud The baudrate for the serial connection. 187 | * @note Only call this function when the allocator is not yet initialized (i.e. before calling @ref start) 188 | */ 189 | void setBaudRate(uint32_t baud) { baudRate = baud; } 190 | 191 | /** 192 | * @brief Send a 'ping' to retrieve a response time. Useful for debugging. 193 | * @return Response time of serial script connected over serial, in microseconds. 194 | */ 195 | uint32_t ping(void) const 196 | { 197 | serram_utils::sendReadCommand(stream, serram_utils::CMD_PING); 198 | const uint32_t starttime = micros(); 199 | while (!serram_utils::waitForCommand(stream, serram_utils::CMD_PING, 1000)) 200 | ; 201 | return micros() - starttime; 202 | } 203 | }; 204 | 205 | typedef SerialVAllocP<> SerialVAlloc; //!< Shortcut to SerialVAllocP with default template arguments 206 | 207 | /** 208 | * @example serial_simple.ino 209 | * This is a simple example sketch showing how to use the serial allocator. 210 | * 211 | * @example serial_input.ino 212 | * This example shows how to use serial input over a port shared with the serial allocator. 213 | */ 214 | } 215 | 216 | #endif // VIRTMEM_SERIAL_ALLOC_H 217 | -------------------------------------------------------------------------------- /virtmem/src/internal/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_ALLOC_H 2 | #define VIRTMEM_ALLOC_H 3 | 4 | /** 5 | @file 6 | @brief virtual memory class header 7 | */ 8 | 9 | #include "base_alloc.h" 10 | #include "config/config.h" 11 | #include "vptr.h" 12 | 13 | namespace virtmem { 14 | 15 | template class VPtr; 16 | 17 | /** 18 | * @brief Base template class for virtual memory allocators. 19 | * 20 | * This template class is used as parent class for all allocators. Most of the actual code is 21 | * defined in BaseVAlloc, while this class only contains code dependent upon template 22 | * parameters (i.e. page settings). 23 | * 24 | * @tparam Properties Allocator properties, see DefaultAllocProperties 25 | * @tparam Derived Dummy parameter, used to create unique singleton instances for each derived class. 26 | */ 27 | template 28 | class VAlloc : public BaseVAlloc 29 | { 30 | LockPage smallPagesData[Properties::smallPageCount]; 31 | LockPage mediumPagesData[Properties::mediumPageCount]; 32 | LockPage bigPagesData[Properties::bigPageCount]; 33 | #ifdef NVALGRIND 34 | uint8_t smallPagePool[Properties::smallPageCount * Properties::smallPageSize] __attribute__ ((aligned (sizeof(TAlign)))); 35 | uint8_t mediumPagePool[Properties::mediumPageCount * Properties::mediumPageSize] __attribute__ ((aligned (sizeof(TAlign)))); 36 | uint8_t bigPagePool[Properties::bigPageCount * Properties::bigPageSize] __attribute__ ((aligned (sizeof(TAlign)))); 37 | #else 38 | uint8_t smallPagePool[Properties::smallPageCount * (Properties::smallPageSize + valgrindPad * 2)]; 39 | uint8_t mediumPagePool[Properties::mediumPageCount * (Properties::mediumPageSize + valgrindPad * 2)]; 40 | uint8_t bigPagePool[Properties::bigPageCount * (Properties::bigPageSize + valgrindPad * 2)]; 41 | #endif 42 | static VAlloc *instance; 43 | 44 | protected: 45 | VAlloc(void) 46 | { 47 | ASSERT(!instance); 48 | instance = this; 49 | #ifdef NVALGRIND 50 | initSmallPages(smallPagesData, &smallPagePool[0], Properties::smallPageCount, Properties::smallPageSize); 51 | initMediumPages(mediumPagesData, &mediumPagePool[0], Properties::mediumPageCount, Properties::mediumPageSize); 52 | initBigPages(bigPagesData, &bigPagePool[0], Properties::bigPageCount, Properties::bigPageSize); 53 | #else 54 | initSmallPages(smallPagesData, &smallPagePool[pad], Properties::smallPageCount, Properties::smallPageSize); 55 | initMediumPages(mediumPagesData, &mediumPagePool[pad], Properties::mediumPageCount, Properties::mediumPageSize); 56 | initBigPages(bigPagesData, &bigPagePool[pad], Properties::bigPageCount, Properties::bigPageSize); 57 | VALGRIND_MAKE_MEM_NOACCESS(&smallPagePool[0], pad); VALGRIND_MAKE_MEM_NOACCESS(&smallPagePool[Properties::smallPageCount * Properties::smallPageSize + pad], pad); 58 | VALGRIND_MAKE_MEM_NOACCESS(&mediumPagePool[0], pad); VALGRIND_MAKE_MEM_NOACCESS(&mediumPagePool[Properties::mediumPageCount * Properties::mediumPageSize + pad], pad); 59 | VALGRIND_MAKE_MEM_NOACCESS(&bigPagePool[0], pad); VALGRIND_MAKE_MEM_NOACCESS(&bigPagePool[Properties::bigPageCount * Properties::bigPageSize + pad], pad); 60 | #endif 61 | } 62 | ~VAlloc(void) { instance = 0; } 63 | 64 | public: 65 | /** 66 | * @brief Returns a pointer to the instance of the class. 67 | * 68 | * All allocator classes are singletons: only one instance can exist for a particular allocator. 69 | * 70 | * Note that, since allocators are template classes, one instance is generated for every set of 71 | * unique template parameters. For example: 72 | * @code{.cpp} 73 | * virtmem::SDVAlloc alloc1; // allocator with default template parameters 74 | * virtmem::SDVAllocP alloc2; // allocator with custom template parameter 75 | * @endcode 76 | * In this case, `alloc1` and `alloc2` are variables with a *different* type, hence getInstance() 77 | * will return a different instance for both classes. 78 | */ 79 | static VAlloc *getInstance(void) { return instance; } 80 | 81 | // C style malloc/free 82 | /** 83 | * @brief Allocates memory from the linked virtual memory allocator 84 | * @tparam T The data type to allocate for. 85 | * @param size The number of bytes to allocate. By default this is the size of the type pointed to. 86 | * @return Virtual pointer pointing to allocated memory. 87 | * 88 | * This function is the equivelant of the C `malloc` function. To allocate memory for an 89 | * array multiply the array size by the size of the type, for instance: 90 | * @code 91 | * // allocate array of 10 integers. 92 | * vptr = virtmem::VPtr::alloc(10 * sizeof(int)); 93 | * @endcode 94 | * @sa free, newClass, newArray, BaseVAlloc::allocRaw 95 | */ 96 | template VPtr alloc(VPtrSize size=sizeof(T)) 97 | { 98 | virtmem::VPtr ret; 99 | ret.setRawNum(allocRaw(size)); 100 | return ret; 101 | } 102 | 103 | /** 104 | * @brief Frees a block of virtual memory 105 | * @param p virtual pointer that points to block to be freed 106 | * 107 | * This function is the equivelant of the C `free` function. The pointer will be set to null. 108 | * @sa alloc, deleteClass, deleteArray, BaseVAlloc::freeRaw 109 | */ 110 | template void free(VPtr &p) 111 | { 112 | freeRaw(p.getRawNum()); 113 | p.setRawNum(0); 114 | } 115 | 116 | // C++ style new/delete --> call constructors (by placement new) and destructors 117 | /** 118 | * @brief Allocates memory and constructs data type 119 | * @tparam T The data type to allocate for. 120 | * @param size The number of bytes to allocate. By default this is the size of the type pointed to. 121 | * @return Virtual pointer pointing to allocated memory. 122 | * 123 | * This function is similar to the C++ `new` operator. Similar to \ref alloc, this function will 124 | * allocate a block of virtual memory. Subsequently, the default constructor of the data type is 125 | * called. For this reason, this function is typically used for C++ classes. 126 | * @note This function should be used together with \ref deleteClass. 127 | * @sa deleteClass, newArray, alloc 128 | */ 129 | template VPtr newClass(VPtrSize size=sizeof(T)) 130 | { 131 | virtmem::VPtr ret = alloc(size); 132 | T *ptr = static_cast(read(ret.getRawNum(), sizeof(T))); 133 | new (ptr) T; // UNDONE: can this be ro? 134 | return ret; 135 | } 136 | 137 | /** 138 | * @brief Destructs data type and frees memory 139 | * @param p virtual pointer to delete 140 | * 141 | * This function is similar to the C++ `delete` operator. After calling the destructor of the 142 | * data type the respective memory block will be freed. 143 | * @note This function should be used together with \ref newClass. 144 | * @sa newClass, deleteArray, free 145 | */ 146 | template void deleteClass(VPtr &p) 147 | { 148 | p->~T(); 149 | freeRaw(p.getRawNum()); 150 | } 151 | 152 | // C++ style new []/delete [] --> calls constructors and destructors 153 | // the size of the array (necessary for destruction) is stored at the beginning of the block. 154 | // A pointer offset from this is returned. 155 | /** 156 | * @brief Allocates and constructs an array of objects. 157 | * @param elements the size of the array 158 | * @return Virtual pointer to start of the array 159 | * 160 | * This function is similar to the C++ new [] operator. After allocating sufficient size for the 161 | * array, the default constructor will be called for each object. 162 | * @note This function should be used together with \ref deleteArray. 163 | * @sa deleteArray, newClass, alloc 164 | */ 165 | template VPtr newArray(VPtrSize elements) 166 | { 167 | VPtrNum p = allocRaw(sizeof(T) * elements + sizeof(VPtrSize)); 168 | write(p, &elements, sizeof(VPtrSize)); 169 | p += sizeof(VPtrSize); 170 | for (VPtrSize s=0; s ret; 174 | ret.setRawNum(p); 175 | return ret; 176 | } 177 | /** 178 | * @brief Destructs and frees an array of objects. 179 | * @param p Virtual pointer to array that should be deleted 180 | * 181 | * This function is similar to the C++ `delete []` operator. After calling all the 182 | * destructors of each object, the respective memory block will be freed. 183 | * @note This function should be used together with \ref newArray. 184 | * @sa newArray, deleteClass, free 185 | */ 186 | template void deleteArray(VPtr &p) 187 | { 188 | const VPtrNum soffset = p.getRawNum() - sizeof(VPtrSize); // pointer to size offset 189 | VPtrSize *size = static_cast(read(soffset, sizeof(VPtrSize))); 190 | for (VPtrSize s=0; s<*size; ++s) 191 | { 192 | T *ptr = static_cast(read(p.getRawNum() + (s * sizeof(T)), sizeof(T))); 193 | ptr->~T(); 194 | } 195 | freeRaw(soffset); // soffset points at beginning of actual block 196 | } 197 | 198 | /** 199 | * @struct TVPtr 200 | * @brief Generalized shortcut to virtual pointer type linked to this allocator. 201 | * 202 | * Example: 203 | * @code{.cpp} 204 | * virtmem::SDAlloc::TVPtr::type vIntPtr; // virtual pointer to int, linked to SD allocator. 205 | * @endcode 206 | * 207 | * @tparam T Type of virtual pointer 208 | */ 209 | template struct TVPtr { typedef virtmem::VPtr type; }; 210 | 211 | #ifdef VIRTMEM_CPP11 212 | /** 213 | * Similar to TVPtr, but slightly shorter syntax (C++11 only) 214 | * Example: 215 | * @code{.cpp} 216 | * virtmem::SDAlloc::VPtr vIntPtr; // virtual pointer to int, linked to SD allocator. 217 | * @endcode 218 | * 219 | * @tparam T Type of virtual pointer 220 | */ 221 | template using VPtr = virtmem::VPtr; 222 | #endif 223 | }; 224 | 225 | template 226 | VAlloc *VAlloc::instance = 0; 227 | 228 | } 229 | 230 | #endif // VIRTMEM_ALLOC_H 231 | -------------------------------------------------------------------------------- /virtmem/src/internal/vptr.h: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_VPTR_H 2 | #define VIRTMEM_VPTR_H 3 | 4 | /** 5 | @file 6 | @brief This header contains the class definition of the VPtr class 7 | */ 8 | 9 | #include "internal/alloc.h" 10 | #include "base_vptr.h" 11 | #include "config/config.h" 12 | #include "utils.h" 13 | 14 | #include 15 | 16 | namespace virtmem { 17 | 18 | template class VPtr; 19 | 20 | namespace private_utils { 21 | 22 | template struct Dereferenced { typedef T type; }; // dummy for non pointers 23 | template struct Dereferenced { typedef T *type; }; // dummy for function pointers. // UNDONE: only handles 1 arg 24 | template struct Dereferenced { typedef T type; }; 25 | template struct Dereferenced > { typedef T type; }; 26 | 27 | // get pointer to variable, use char & cast to override any operator & overload 28 | template T *pointerTo(const T &val) { return (T *)&(char &)val; } 29 | } 30 | 31 | /** 32 | * @brief Virtual pointer class that provides an interface to access virtual much like 'regular pointers'. 33 | * 34 | * This class provides an easy to use interface to access from virtual memory. The class provides 35 | * functionality such as dereferencing data, array access, memory (de)allocation and 36 | * pointer arithmetic. This class is designed in such a way that it can be 37 | * treated mostly as 'plain old data' (POD) and can therefore be used in an `union` as well. 38 | * 39 | * @tparam T The type of the data this pointer points to (e.g. char, int, a struct etc...) 40 | * @tparam TA The allocator type that contains the virtual memory pool where the pointed data resides. 41 | * 42 | * @sa BaseVPtr, TSPIRAMVirtPtr, TSDVirtPtr, TSerRAMVirtPtr, TStaticVPtr and TStdioVirtPtr 43 | */ 44 | 45 | template class VPtr : public BaseVPtr 46 | { 47 | public: 48 | typedef T *TPtr; //!< (non virtual) pointer type to the base type of this virtual pointer. 49 | typedef TA Allocator; //!< Allocator type used by this virtual pointer class. @sa getAlloc 50 | 51 | private: 52 | typedef VPtr ThisVPtr; 53 | 54 | static T *read(PtrNum p) 55 | { 56 | if (!p) 57 | return 0; 58 | #ifdef VIRTMEM_WRAP_CPOINTERS 59 | if (isWrapped(p)) 60 | return static_cast(BaseVPtr::unwrap(p)); 61 | #endif 62 | return static_cast(getAlloc()->read(p, sizeof(T))); 63 | } 64 | T *read(void) const { return read(ptr); } 65 | 66 | static void write(PtrNum p, const T *d) 67 | { 68 | #ifdef VIRTMEM_WRAP_CPOINTERS 69 | if (isWrapped(p)) 70 | *(static_cast(BaseVPtr::unwrap(p))) = *d; 71 | else 72 | #endif 73 | getAlloc()->write(p, d, sizeof(T)); 74 | } 75 | void write(const T *d) { write(ptr, d); } 76 | 77 | ThisVPtr copy(void) const { ThisVPtr ret; ret.ptr = ptr; return ret; } 78 | template friend class VPtrLock; 79 | 80 | public: 81 | /** 82 | * @brief Proxy class returned when dereferencing virtual pointers. 83 | * @sa @ref aAccess 84 | */ 85 | class ValueWrapper 86 | { 87 | PtrNum ptr; 88 | 89 | ValueWrapper(PtrNum p) : ptr(p) { } 90 | ValueWrapper(const ValueWrapper &); 91 | 92 | template friend class VPtr; 93 | 94 | public: 95 | /** 96 | * @name Proxy operators 97 | * @{ 98 | */ 99 | inline operator T(void) const { return *read(ptr); } 100 | template VIRTMEM_EXPLICIT inline operator T2(void) const { return static_cast(operator T()); } 101 | 102 | // ValueWrapper &operator=(const ValueWrapper &v) 103 | // HACK: 'allow' non const assignment of types. In reality this makes sure we don't define 104 | // the assignment operator twice for const types (where T == const T) 105 | ValueWrapper &operator=(const typename VPtr::type, TA>::ValueWrapper &v) 106 | { 107 | ASSERT(ptr != 0); 108 | if (ptr != v.ptr) 109 | { 110 | const T val = *read(v.ptr); 111 | write(ptr, &val); 112 | } 113 | return *this; 114 | } 115 | // const conversion 116 | ValueWrapper &operator=(const typename VPtr::ValueWrapper &v) 117 | { 118 | ASSERT(ptr != 0); 119 | if (ptr != v.ptr) 120 | { 121 | const T val = *read(v.ptr); 122 | write(ptr, &val); 123 | } 124 | return *this; 125 | } 126 | 127 | inline ValueWrapper &operator=(const T &v) { write(ptr, &v); return *this; } 128 | inline ThisVPtr operator&(void) { ThisVPtr ret; ret.ptr = ptr; return ret; } 129 | 130 | // allow pointer to pointer access 131 | // UNDONE: use member wrapper here? 132 | inline T operator->(void) { return operator T(); } 133 | inline const T operator->(void) const { return operator T(); } 134 | inline typename VPtr::type, TA>::ValueWrapper operator*(void) { return *operator T(); } 135 | inline const typename VPtr::type, TA>::ValueWrapper operator*(void) const { return *operator T(); } 136 | 137 | typename VPtr::type, TA>::ValueWrapper operator[](int i) 138 | { return operator T()[i]; } 139 | const typename VPtr::type, TA>::ValueWrapper operator[](int i) const 140 | { return operator T()[i]; } 141 | 142 | template inline bool operator==(const T2 &v) const { return operator T() == v; } 143 | template inline bool operator!=(const T2 &v) const { return operator T() != v; } 144 | 145 | ValueWrapper &operator+=(int n) { T newv = operator T() + n; write(ptr, private_utils::pointerTo(newv)); return *this; } 146 | ValueWrapper &operator-=(int n) { return operator+=(-n); } 147 | ValueWrapper &operator*=(int n) { T newv = operator T() * n; write(ptr, private_utils::pointerTo(newv)); return *this; } 148 | ValueWrapper &operator/=(int n) { T newv = operator T() / n; write(ptr, private_utils::pointerTo(newv)); return *this; } 149 | ValueWrapper &operator++(void) { return operator +=(1); } 150 | T operator++(int) { T ret = operator T(); operator++(); return ret; } 151 | //! @} 152 | }; 153 | 154 | // Based on Stroustrup's general wrapper paper (http://www.stroustrup.com/wrapper.pdf) 155 | /** 156 | * @brief Proxy class used when member access is requested on a virtual pointer. 157 | * @sa @ref aAccess 158 | */ 159 | class MemberWrapper 160 | { 161 | const PtrNum ptr; 162 | 163 | template friend class VPtr; 164 | 165 | MemberWrapper(PtrNum p) : ptr(p) { } 166 | MemberWrapper(const MemberWrapper &); 167 | 168 | MemberWrapper &operator=(const MemberWrapper &); // No assignment 169 | 170 | public: 171 | ~MemberWrapper(void) 172 | { 173 | #ifdef VIRTMEM_WRAP_CPOINTERS 174 | if (!isWrapped(ptr)) 175 | #endif 176 | getAlloc()->releaseLock(ptr); 177 | } 178 | 179 | /** 180 | * @name Access operators 181 | * @{ 182 | */ 183 | 184 | T *operator->(void) 185 | { 186 | #ifdef VIRTMEM_WRAP_CPOINTERS 187 | if (isWrapped(ptr)) 188 | return static_cast(BaseVPtr::unwrap(ptr)); 189 | #endif 190 | return static_cast(getAlloc()->makeDataLock(getPtrNum(ptr), sizeof(T))); 191 | } 192 | const T *operator->(void) const 193 | { 194 | #ifdef VIRTMEM_WRAP_CPOINTERS 195 | if (isWrapped(ptr)) 196 | return static_cast(BaseVPtr::unwrap(ptr)); 197 | #endif 198 | return static_cast(getAlloc()->makeDataLock(getPtrNum(ptr), sizeof(T), true)); 199 | } 200 | //! @} 201 | }; 202 | 203 | #ifdef VIRTMEM_CPP11 204 | // Can only use these constructors on C++11, otherwise vptrs cannot be used with unions 205 | VPtr(void) = default; // Must have this to remain union compatible 206 | VPtr(NILL_t) : BaseVPtr(nullptr) { } //!< Construct from NILL/nullptr (C++11 only) 207 | #endif 208 | 209 | #ifdef VIRTMEM_WRAP_CPOINTERS 210 | /** 211 | * @name Members related to regular pointer wrapping 212 | * The following functions / operators are only defined when \ref VIRTMEM_WRAP_CPOINTERS is set. 213 | * @{ 214 | */ 215 | 216 | /** 217 | * @brief Wraps a regular pointer. 218 | * 219 | * Regular (non virtual) pointers can be stored inside a virtual pointer (wrapping). 220 | * In this situation, arithmetic, access, etc. will be proxied to the regular pointer. 221 | * This is useful when using code that mixes both virtual and non-virtual pointers. 222 | * 223 | * Example: 224 | * @code 225 | * int data[] = { 1, 2, 3, 4 }; 226 | * typedef virtmem::VPtr vptrType; 227 | * vptrType vptr = vptrType::wrap(data); 228 | * vptr += 2; 229 | * Serial.println(*vptr); // prints "3" 230 | * @endcode 231 | * @param p regular pointer to wrap 232 | * @return A virtual pointer wrapping the specified pointer. 233 | * @sa unwrap 234 | * @note \ref VIRTMEM_WRAP_CPOINTERS needs to be defined (e.g. in config.h) to enable this function. 235 | */ 236 | static ThisVPtr wrap(const T *p) 237 | { 238 | ThisVPtr ret; 239 | ret.ptr = getWrapped(reinterpret_cast(p)); 240 | return ret; 241 | } 242 | /** 243 | * @brief Provide access to wrapped regular pointer. 244 | * @param p virtual pointer that wraps a regular pointer 245 | * @return regular pointer wrapped by specified virtual pointer. 246 | * @sa wrap 247 | * @note \ref VIRTMEM_WRAP_CPOINTERS needs to be defined (e.g. in config.h) to enable this function. 248 | */ 249 | static T *unwrap(const ThisVPtr &p) { return static_cast(BaseVPtr::unwrap(p)); } 250 | /** 251 | * @brief Provide access to wrapped regular pointer (non static version). 252 | * @sa VPtr::unwrap(const ThisVPtr &p), wrap 253 | * @note \ref VIRTMEM_WRAP_CPOINTERS needs to be defined (e.g. in config.h) to enable this function. 254 | */ 255 | T *unwrap(void) { return static_cast(BaseVPtr::unwrap(ptr)); } 256 | /** 257 | * @brief Provide access to wrapped regular pointer (non static const version). 258 | * @sa VPtr::unwrap(const ThisVPtr &p), wrap 259 | * @note \ref VIRTMEM_WRAP_CPOINTERS needs to be defined (e.g. in config.h) to enable this function. 260 | */ 261 | const T *unwrap(void) const { return static_cast(BaseVPtr::unwrap(ptr)); } 262 | 263 | #ifdef VIRTMEM_VIRT_ADDRESS_OPERATOR 264 | /** 265 | * @brief Returns a virtual pointer that has the memory address of this virtual pointer wrapped. 266 | * @note This operator is *only* overloaded when \ref VIRTMEM_WRAP_CPOINTERS and \ref VIRTMEM_VIRT_ADDRESS_OPERATOR 267 | * are defined (e.g. in config.h). 268 | * @sa \ref VIRTMEM_VIRT_ADDRESS_OPERATOR 269 | */ 270 | VPtr operator&(void) { VPtr ret = ret.wrap(this); return ret; } 271 | 272 | /** 273 | * @brief Returns a regular pointer to the address of this virtual pointer 274 | * @note This function is only defined when \ref VIRTMEM_WRAP_CPOINTERS and \ref VIRTMEM_VIRT_ADDRESS_OPERATOR 275 | * are defined (e.g. in config.h). 276 | * @sa \ref VIRTMEM_VIRT_ADDRESS_OPERATOR 277 | */ 278 | const VPtr *addressOf(void) const { return this; } 279 | VPtr *addressOf(void) { return this; } //!< @copydoc addressOf(void) const 280 | #endif 281 | // @} 282 | 283 | #endif 284 | 285 | /** 286 | * @name Dereference operators 287 | * The following operators are used for accessing the data pointed to by this virtual pointer. 288 | * The returned value is a proxy class, which mostly acts as the data itself. 289 | * @sa aAccess 290 | * @{ 291 | */ 292 | ValueWrapper operator*(void) { return ValueWrapper(ptr); } 293 | MemberWrapper operator->(void) { return MemberWrapper(ptr); } 294 | const MemberWrapper operator->(void) const { return MemberWrapper(ptr); } 295 | const ValueWrapper operator[](int i) const { return ValueWrapper(ptr + (i * sizeof(T))); } 296 | ValueWrapper operator[](int i) { return ValueWrapper(ptr + (i * sizeof(T))); } 297 | //! @} 298 | 299 | /** 300 | * @name Const conversion operators 301 | * @{ 302 | */ 303 | inline operator VPtr(void) { VPtr ret; ret.ptr = ptr; return ret; } 304 | // pointer to pointer conversion 305 | template VIRTMEM_EXPLICIT inline operator VPtr(void) { VPtr ret; ret.ptr = ptr; return ret; } 306 | //! @} 307 | 308 | /** 309 | * @name Pointer arithmetic 310 | * These operators can be used for pointer arithmetics. 311 | * @{ 312 | */ 313 | // NOTE: int cast is necessary to deal with negative numbers 314 | ThisVPtr &operator+=(int n) { ptr += (n * (int)sizeof(T)); return *this; } 315 | inline ThisVPtr &operator++(void) { return operator +=(1); } 316 | inline ThisVPtr operator++(int) { ThisVPtr ret = copy(); operator++(); return ret; } 317 | inline ThisVPtr &operator-=(int n) { return operator +=(-n); } 318 | inline ThisVPtr &operator--(void) { return operator -=(1); } 319 | inline ThisVPtr operator--(int) { ThisVPtr ret = copy(); operator--(); return ret; } 320 | inline ThisVPtr operator+(int n) const { return (copy() += n); } 321 | inline ThisVPtr operator-(int n) const { return (copy() -= n); } 322 | int operator-(const ThisVPtr &other) const { return (ptr - other.ptr) / sizeof(T); } 323 | //! @} 324 | // inline bool operator!=(const TVirtPtr &p) const { return ptr != p.ptr; } UNDONE: need this? 325 | 326 | /** 327 | * @brief Returns instance of allocator bound to this virtual pointer. 328 | * @sa VAlloc::getInstance 329 | */ 330 | static inline Allocator *getAlloc(void) { return static_cast(Allocator::getInstance()); } 331 | }; 332 | 333 | } 334 | 335 | 336 | #endif // VIRTMEM_VPTR_H 337 | -------------------------------------------------------------------------------- /test/test_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "virtmem.h" 2 | #include "alloc/static_alloc.h" 3 | #include "alloc/stdio_alloc.h" 4 | #include "test.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | using namespace virtmem; 11 | 12 | // helper function, can't use assertions directly 13 | template ::testing::AssertionResult wrapperIsNull(const VPtr &p) 14 | { 15 | if (p == 0) 16 | return ::testing::AssertionSuccess(); 17 | else 18 | return ::testing::AssertionFailure() << "ptr wrapper not null: " << (uint64_t)p.getRawNum(); 19 | } 20 | 21 | struct TestStruct 22 | { 23 | int x, y; 24 | 25 | // For membr ptr tests 26 | struct SubStruct { int x, y; } sub; 27 | CharVirtPtr vbuf; 28 | char buf[10]; // NOTE: this has to be last for MembrAssignTest 29 | }; 30 | 31 | bool operator==(const TestStruct &s1, const TestStruct &s2) { return s1.x == s2.x && s1.y == s2.y; } 32 | bool operator!=(const TestStruct &s1, const TestStruct &s2) { return s1.x != s2.x && s1.y != s2.y; } 33 | 34 | std::ostream &operator<<(std::ostream &os, const TestStruct& s) 35 | { 36 | return os << "(" << s.x << ", " << s.y << ")"; 37 | } 38 | 39 | class CTestClass 40 | { 41 | public: 42 | static int constructedClasses; 43 | 44 | CTestClass(void) { ++constructedClasses; } 45 | ~CTestClass(void) { --constructedClasses; } 46 | }; 47 | 48 | int CTestClass::constructedClasses = 0; 49 | 50 | //typedef ::testing::Types WrapTypes; 51 | typedef ::testing::Types WrapTypes; 52 | TYPED_TEST_CASE(VPtrFixture, WrapTypes); 53 | 54 | template class LimitedWrapFixture: public VPtrFixture { }; 55 | typedef ::testing::Types LimitedWrapTypes; 56 | TYPED_TEST_CASE(LimitedWrapFixture, LimitedWrapTypes); 57 | 58 | typedef VPtrFixture IntWrapFixture; 59 | typedef VPtrFixture ClassWrapFixture; 60 | typedef VPtrFixture StructWrapFixture; 61 | 62 | TYPED_TEST(VPtrFixture, SimpleWrapTest) 63 | { 64 | EXPECT_TRUE(wrapperIsNull(this->vptr)); 65 | EXPECT_EQ(this->vptr, NILL); 66 | 67 | this->vptr = VAllocFixture::valloc.alloc(); 68 | EXPECT_FALSE(wrapperIsNull(this->vptr)); 69 | ASSERT_NE(this->vptr, NILL); 70 | 71 | TypeParam val; 72 | memset(&val, 10, sizeof(val)); 73 | *this->vptr = val; 74 | EXPECT_EQ((TypeParam)*this->vptr, val); 75 | this->valloc.clearPages(); 76 | EXPECT_EQ((TypeParam)*this->vptr, val); 77 | 78 | typename StdioVAlloc::TVPtr::type wrp2 = VAllocFixture::valloc.alloc(); 79 | *wrp2 = *this->vptr; 80 | EXPECT_EQ((TypeParam)*this->vptr, (TypeParam)*wrp2); 81 | 82 | VAllocFixture::valloc.free(this->vptr); 83 | EXPECT_TRUE(wrapperIsNull(this->vptr)); 84 | EXPECT_EQ(this->vptr, NILL); 85 | } 86 | 87 | TYPED_TEST(VPtrFixture, ConstWrapTest) 88 | { 89 | this->vptr = VAllocFixture::valloc.alloc(); 90 | TypeParam val; 91 | memset(&val, 10, sizeof(val)); 92 | *this->vptr = val; 93 | 94 | typename StdioVAlloc::TVPtr::type wrp2 = VAllocFixture::valloc.alloc(); 95 | *wrp2 = *this->vptr; 96 | typename StdioVAlloc::TVPtr::type cwrp2 = wrp2; 97 | EXPECT_EQ((TypeParam)*wrp2, (TypeParam)*cwrp2); 98 | EXPECT_EQ((TypeParam)*cwrp2, (TypeParam)*wrp2); 99 | 100 | memset(&val, 20, sizeof(val)); 101 | *this->vptr = val; 102 | // fails if assignment yielded shallow copy 103 | EXPECT_NE((TypeParam)*this->vptr, (TypeParam)*cwrp2) << "ptrs: " << (uint64_t)this->vptr.getRawNum() << "/" << (uint64_t)cwrp2.getRawNum(); 104 | 105 | *this->vptr = *cwrp2; 106 | EXPECT_EQ((TypeParam)*this->vptr, (TypeParam)*cwrp2); 107 | } 108 | 109 | TYPED_TEST(VPtrFixture, BaseWrapTest) 110 | { 111 | this->vptr = VAllocFixture::valloc.alloc(); 112 | 113 | BaseVPtr basewrp = this->vptr; 114 | EXPECT_EQ(basewrp, this->vptr); 115 | typename StdioVAlloc::TVPtr::type wrp = static_cast::type>(basewrp); 116 | EXPECT_EQ(basewrp, wrp); 117 | EXPECT_EQ(wrp, this->vptr); 118 | } 119 | 120 | #ifdef VIRTMEM_WRAP_CPOINTERS 121 | TYPED_TEST(VPtrFixture, WrapWrapTest) 122 | { 123 | TypeParam val; 124 | memset(&val, 10, sizeof(val)); 125 | this->vptr = this->vptr.wrap(&val); 126 | 127 | EXPECT_EQ(this->vptr.unwrap(), &val); 128 | EXPECT_EQ((TypeParam)*this->vptr, val); 129 | 130 | this->valloc.clearPages(); 131 | 132 | EXPECT_EQ(this->vptr.unwrap(), &val); 133 | EXPECT_EQ((TypeParam)*this->vptr, val); 134 | } 135 | #endif 136 | 137 | TYPED_TEST(LimitedWrapFixture, ArithmeticTest) 138 | { 139 | const int size = 10, start = 10; // Offset from zero to avoid testing to zero initialized values 140 | 141 | this->vptr = VAllocFixture::valloc.alloc(size * sizeof(TypeParam)); 142 | typename StdioVAlloc::TVPtr::type wrpp = this->vptr; 143 | for (int i=start; ivptr; 150 | this->valloc.clearPages(); 151 | 152 | for (int i=start; ivptr; 159 | EXPECT_EQ(wrpp, this->vptr); 160 | wrpp += 2; wrpp += -2; 161 | EXPECT_EQ(wrpp, this->vptr); 162 | EXPECT_EQ(*wrpp, *this->vptr); 163 | 164 | typename StdioVAlloc::TVPtr::type wrpp2 = this->vptr; 165 | EXPECT_EQ(*wrpp, *(wrpp2++)); 166 | EXPECT_EQ(*(++wrpp), *wrpp2); 167 | EXPECT_EQ(wrpp, wrpp2); 168 | --wrpp; 169 | EXPECT_EQ(*wrpp, *this->vptr); 170 | EXPECT_NE(wrpp, wrpp2--); 171 | EXPECT_EQ(wrpp, wrpp2); 172 | 173 | wrpp2 = wrpp + 2; 174 | EXPECT_GT(wrpp2, wrpp); 175 | EXPECT_LT(wrpp, wrpp2); 176 | EXPECT_EQ(wrpp + 2, wrpp2); 177 | 178 | EXPECT_EQ(wrpp2 - 2, wrpp); 179 | EXPECT_EQ((wrpp2 - wrpp), 2); 180 | 181 | EXPECT_EQ((wrpp += 2), wrpp2); 182 | EXPECT_EQ((wrpp2 -= 2), this->vptr); 183 | 184 | wrpp = this->vptr; 185 | wrpp2 = wrpp + 1; 186 | EXPECT_LE(wrpp, this->vptr); 187 | EXPECT_LE(wrpp, wrpp2); 188 | EXPECT_GE(wrpp2, this->vptr); 189 | EXPECT_GE(wrpp2, wrpp); 190 | } 191 | 192 | #ifdef VIRTMEM_WRAP_CPOINTERS 193 | TYPED_TEST(LimitedWrapFixture, WrappedArithmeticTest) 194 | { 195 | const int size = 10, start = 10; 196 | 197 | TypeParam *buf = new TypeParam[size]; 198 | this->vptr = this->vptr.wrap(buf); 199 | TypeParam *bufp = buf; 200 | typename StdioVAlloc::TVPtr::type wrpp = this->vptr; 201 | 202 | for (int i=start; ivptr; 209 | this->valloc.clearPages(); 210 | 211 | for (int i=start; ivptr), (bufp - buf)); 240 | 241 | delete [] buf; 242 | } 243 | #endif 244 | 245 | TEST_F(IntWrapFixture, AlignmentTest) 246 | { 247 | const int bufsize = 17; 248 | 249 | this->vptr = valloc.alloc(); 250 | CharVirtPtr buf = valloc.alloc(bufsize); 251 | CharVirtPtr unalignedbuf = &buf[1]; 252 | valloc.clearPages(); 253 | volatile char c = *unalignedbuf; // force load of unaligned address 254 | ASSERT_EQ(reinterpret_cast(valloc.read(this->vptr.getRawNum(), sizeof(int))) & (sizeof(int)-1), 0); 255 | 256 | // check if int is still aligned after locking a big page 257 | valloc.clearPages(); 258 | valloc.makeDataLock(unalignedbuf.getRawNum(), valloc.getBigPageSize(), true); 259 | valloc.releaseLock(unalignedbuf.getRawNum()); 260 | 261 | ASSERT_EQ(reinterpret_cast(valloc.read(this->vptr.getRawNum(), sizeof(int))) & (sizeof(int)-1), 0); 262 | } 263 | 264 | TEST_F(ClassWrapFixture, ClassAllocTest) 265 | { 266 | ASSERT_EQ(CTestClass::constructedClasses, 0); 267 | 268 | StdioVAlloc::TVPtr::type cptr = valloc.newClass(); 269 | EXPECT_EQ(CTestClass::constructedClasses, 1); 270 | valloc.deleteClass(cptr); 271 | EXPECT_EQ(CTestClass::constructedClasses, 0); 272 | } 273 | 274 | TEST_F(ClassWrapFixture, ClassArrayAllocTest) 275 | { 276 | const int elements = 10; 277 | 278 | StdioVAlloc::TVPtr::type cptr = valloc.newArray(elements); 279 | EXPECT_EQ(CTestClass::constructedClasses, elements); 280 | valloc.deleteArray(cptr); 281 | EXPECT_EQ(CTestClass::constructedClasses, 0); 282 | } 283 | 284 | // Tests for VPtr::ValueWrapper 285 | TEST_F(IntWrapFixture, OperatorTest) 286 | { 287 | const int start = 100; 288 | 289 | StdioVAlloc::TVPtr::type vptr = valloc.alloc(); 290 | int i = start; 291 | 292 | EXPECT_EQ(*vptr = start, start); 293 | EXPECT_EQ(*vptr + 1, i + 1); 294 | EXPECT_EQ(*vptr - 1, i - 1); 295 | EXPECT_EQ(*vptr * 2, i * 2); 296 | EXPECT_EQ(*vptr / 2, i / 2); 297 | 298 | EXPECT_EQ(i, *vptr); 299 | EXPECT_EQ(i + 1, *vptr + 1); 300 | EXPECT_EQ(i - 1, *vptr - 1); 301 | EXPECT_EQ(i * 2, *vptr * 2); 302 | EXPECT_EQ(i / 2, *vptr / 2); 303 | 304 | i += 10; 305 | *vptr += 10; 306 | EXPECT_EQ(*vptr, i); 307 | 308 | i -= 10; 309 | *vptr -= 10; 310 | EXPECT_EQ(*vptr, i); 311 | 312 | ++i; 313 | EXPECT_NE(*vptr, i); 314 | EXPECT_NE(i, *vptr); 315 | EXPECT_LT(*vptr, i); 316 | EXPECT_LE(*vptr, i); 317 | EXPECT_GT(i, *vptr); 318 | EXPECT_GE(i, *vptr); 319 | 320 | *vptr += 10; 321 | EXPECT_EQ(*vptr, i + 9); 322 | EXPECT_LT(i, *vptr); 323 | EXPECT_LE(i, *vptr); 324 | EXPECT_GT(*vptr, i); 325 | EXPECT_GE(*vptr, i); 326 | 327 | *vptr -= 9; 328 | EXPECT_EQ(*vptr, i); 329 | 330 | i *= 10; 331 | *vptr *= 10; 332 | EXPECT_EQ(*vptr, i); 333 | 334 | i /= 10; 335 | *vptr /= 10; 336 | EXPECT_EQ(*vptr, i); 337 | 338 | i = *vptr = start; 339 | EXPECT_EQ(!*vptr, !i); 340 | EXPECT_EQ(~*vptr, ~i); 341 | EXPECT_EQ(*vptr | 10, i | 10); 342 | EXPECT_EQ(*vptr & 10, i & 10); 343 | EXPECT_EQ(*vptr ^ 10, i ^ 10); 344 | EXPECT_EQ((int)*vptr << 10, i << 10); // this cast is necessary for google test (ambiguous otherwise) 345 | EXPECT_EQ(*vptr >> 10, i >> 10); 346 | } 347 | 348 | TEST_F(IntWrapFixture, MultiAllocTest) 349 | { 350 | // Second allocator 351 | typedef StaticVAllocP<1024*1024> Alloc2; 352 | Alloc2 valloc2; 353 | valloc2.start(); 354 | 355 | this->vptr = valloc.alloc(); 356 | VPtr vptr2 = valloc2.alloc(); 357 | 358 | *this->vptr = 55; 359 | *vptr2 = (int)*this->vptr; 360 | EXPECT_EQ(*this->vptr, *vptr2); 361 | valloc.clearPages(); 362 | valloc2.clearPages(); 363 | EXPECT_EQ(*this->vptr, *vptr2); 364 | 365 | valloc2.stop(); 366 | } 367 | 368 | TEST_F(StructWrapFixture, MembrAssignTest) 369 | { 370 | this->vptr = valloc.alloc(); 371 | this->vptr->x = 55; 372 | EXPECT_EQ(this->vptr->x, 55); 373 | 374 | // test if usage of other struct fields messes up assignment 375 | this->vptr->y = this->vptr->x * this->vptr->x; 376 | EXPECT_EQ(this->vptr->y, (55*55)); 377 | 378 | // Test partial data structures (useful for unions or partially allocated structs) 379 | // The struct is partially allocated, without the char buffer, while some extra space is requested right after for a virt char buffer 380 | const size_t bufsize = sizeof((TestStruct *)0)->buf; 381 | const size_t vbufsize = sizeof((TestStruct *)0)->vbuf; 382 | const int vbufelements = 64; 383 | 384 | valloc.free(this->vptr); 385 | this->vptr = valloc.alloc(sizeof(TestStruct) - bufsize + vbufsize + vbufelements); 386 | this->vptr->x = 55; this->vptr->y = 66; 387 | this->vptr->vbuf = (CharVirtPtr)getMembrPtr(this->vptr, &TestStruct::vbuf) + vbufsize; // point to end of struct 388 | 389 | const char *str = "Does this somewhat boring sentence compares well?"; 390 | strcpy(this->vptr->vbuf, str); 391 | 392 | // struct is still ok? 393 | EXPECT_EQ(this->vptr->x, 55); 394 | EXPECT_EQ(this->vptr->y, 66); 395 | // vbuf ok? 396 | EXPECT_EQ(strcmp(this->vptr->vbuf, str), 0); 397 | 398 | // UNDONE: test data in partial struct with larger datatype than char 399 | } 400 | 401 | TEST_F(StructWrapFixture, MembrDiffTest) 402 | { 403 | this->vptr = valloc.alloc(); 404 | 405 | const size_t offset_x = offsetof(TestStruct, x); 406 | const size_t offset_y = offsetof(TestStruct, y); 407 | const size_t offset_buf = offsetof(TestStruct, buf); 408 | const size_t offset_vbuf = offsetof(TestStruct, vbuf); 409 | 410 | const UCharVirtPtr vptr_x = static_cast(getMembrPtr(this->vptr, &TestStruct::x)); 411 | const UCharVirtPtr vptr_y = static_cast(getMembrPtr(this->vptr, &TestStruct::y)); 412 | const UCharVirtPtr vptr_buf = static_cast(getMembrPtr(this->vptr, &TestStruct::buf)); 413 | const UCharVirtPtr vptr_vbuf = static_cast(getMembrPtr(this->vptr, &TestStruct::vbuf)); 414 | 415 | const UCharVirtPtr base = static_cast(this->vptr); 416 | EXPECT_EQ(vptr_x - base, offset_x); 417 | EXPECT_EQ(vptr_y - base, offset_y); 418 | EXPECT_EQ(vptr_buf - base, offset_buf); 419 | EXPECT_EQ(vptr_vbuf - base, offset_vbuf); 420 | 421 | // Sub structure 422 | const size_t offset_sub = offsetof(TestStruct, sub); 423 | const size_t offset_sub_x = offset_sub + offsetof(TestStruct::SubStruct, x); 424 | const size_t offset_sub_y = offset_sub + offsetof(TestStruct::SubStruct, y); 425 | 426 | const UCharVirtPtr vptr_sub_x = static_cast(getMembrPtr(this->vptr, &TestStruct::sub, &TestStruct::SubStruct::x)); 427 | const UCharVirtPtr vptr_sub_y = static_cast(getMembrPtr(this->vptr, &TestStruct::sub, &TestStruct::SubStruct::y)); 428 | EXPECT_EQ(vptr_sub_x - base, offset_sub_x); 429 | EXPECT_EQ(vptr_sub_y - base, offset_sub_y); 430 | } 431 | -------------------------------------------------------------------------------- /virtmem/src/internal/vptr_utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VIRTMEM_UTILS_HPP 2 | #define VIRTMEM_UTILS_HPP 3 | 4 | /** 5 | @file 6 | @brief This header file contains several overloads of common C functions that work with virtual pointers. 7 | */ 8 | 9 | #include "config/config.h" 10 | #include "utils.h" 11 | #include "vptr.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace virtmem { 17 | 18 | namespace private_utils { 19 | 20 | template struct TSameType { static const bool flag = false; }; 21 | template struct TSameType { static const bool flag = true; }; 22 | 23 | template struct TVirtPtrTraits { }; 24 | 25 | template struct TVirtPtrTraits > 26 | { 27 | typedef VPtrLock > Lock; 28 | 29 | static bool isWrapped(VPtr p) { return p.isWrapped(); } 30 | static T *unwrap(VPtr p) { return p.unwrap(); } 31 | static bool isVirtPtr(void) { return true; } 32 | static Lock makeLock(VPtr w, VirtPageSize s, bool ro=false) 33 | { return makeVirtPtrLock(w, s, ro); } 34 | static VirtPageSize getLockSize(Lock &l) { return l.getLockSize(); } 35 | static VirtPageSize getPageSize(void) 36 | { return A::getInstance()->getBigPageSize(); } 37 | 38 | }; 39 | 40 | template struct TVirtPtrTraits 41 | { 42 | typedef T** Lock; 43 | 44 | static bool isWrapped(T *) { return false; } 45 | static T *unwrap(T *p) { return p; } 46 | static bool isVirtPtr(void) { return false; } 47 | static Lock makeLock(T *&p, VirtPageSize, __attribute__ ((unused)) bool ro=false) { return &p; } 48 | static VirtPageSize getLockSize(Lock &) { return (VirtPageSize)-1; } 49 | static VirtPageSize getPageSize(void) { return (VirtPageSize)-1; } 50 | }; 51 | 52 | template int ptrDiff(VPtr p1, VPtr p2) { return p2 - p1; } 53 | template int ptrDiff(T1 *p1, T2 *p2) { return p2 - p1; } 54 | template int ptrDiff(T1, T2) { return 0; } // dummy, not used (for mix of regular and virt pointers) 55 | template bool ptrEqual(VPtr p1, VPtr p2) { return p1 == p2; } 56 | template bool ptrEqual(T1 *p1, T2 *p2) { return p1 == p2; } 57 | template bool ptrEqual(T1, T2) { return false; } // mix of virt and regular pointers 58 | 59 | template VirtPageSize getMaxLockSize(T1 p1, T2 p2) 60 | { 61 | VirtPageSize ret = minimal(TVirtPtrTraits::getPageSize(), TVirtPtrTraits::getPageSize()); 62 | 63 | // check for overlap in case both p1 and p2 are virtual pointers from the same allocator 64 | if (TSameType::flag && TVirtPtrTraits::isVirtPtr()) 65 | ret = minimal(ret, static_cast(abs(ptrDiff(p1, p2)))); 66 | 67 | return ret; 68 | } 69 | 70 | 71 | // copier function that works with raw pointers for rawCopy, returns false if copying should be aborted 72 | typedef bool (*RawCopier)(char *, const char *, VPtrSize); 73 | 74 | // Generalized copy for memcpy and strncpy 75 | template T1 rawCopy(T1 dest, T2 src, VPtrSize size, 76 | RawCopier copier) 77 | { 78 | if (size == 0 || ptrEqual(dest, src)) 79 | return dest; 80 | 81 | #ifdef VIRTMEM_WRAP_CPOINTERS 82 | if (!TVirtPtrTraits::isVirtPtr() && !TVirtPtrTraits::isVirtPtr()) 83 | { 84 | // Even though both are pointers, we must unwrap to avoid compile errors with other types 85 | copier(TVirtPtrTraits::unwrap(dest), TVirtPtrTraits::unwrap(src), size); 86 | return dest; 87 | } 88 | else if (TVirtPtrTraits::isWrapped(dest) && TVirtPtrTraits::isWrapped(src)) 89 | { 90 | copier(TVirtPtrTraits::unwrap(dest), TVirtPtrTraits::unwrap(src), size); 91 | return dest; 92 | } 93 | else if (TVirtPtrTraits::isWrapped(dest)) 94 | { 95 | rawCopy(TVirtPtrTraits::unwrap(dest), src, size, copier); 96 | return dest; 97 | } 98 | else if (TVirtPtrTraits::isWrapped(src)) 99 | return rawCopy(dest, TVirtPtrTraits::unwrap(src), size, copier); 100 | #endif 101 | 102 | VPtrSize sizeleft = size; 103 | const VirtPageSize maxlocksize = getMaxLockSize(dest, src); 104 | T1 p1 = dest; 105 | T2 p2 = src; 106 | 107 | while (sizeleft) 108 | { 109 | VirtPageSize cpsize = minimal(static_cast(maxlocksize), sizeleft); 110 | 111 | typename TVirtPtrTraits::Lock l1 = TVirtPtrTraits::makeLock(p1, cpsize); 112 | cpsize = minimal(cpsize, TVirtPtrTraits::getLockSize(l1)); 113 | typename TVirtPtrTraits::Lock l2 = TVirtPtrTraits::makeLock(p2, cpsize, true); 114 | cpsize = minimal(cpsize, TVirtPtrTraits::getLockSize(l2)); 115 | 116 | if (!copier(*l1, *l2, cpsize)) 117 | return dest; 118 | 119 | p1 += cpsize; p2 += cpsize; 120 | sizeleft -= cpsize; 121 | ASSERT(sizeleft <= size); 122 | } 123 | 124 | return dest; 125 | } 126 | 127 | // comparison function for rawCompare 128 | typedef int (*RawComparator)(const char *, const char *, VPtrSize, bool &); 129 | 130 | // Generalized compare for memcmp/strncmp 131 | template int rawCompare(T1 p1, T2 p2, VPtrSize n, RawComparator comparator) 132 | { 133 | if (n == 0 || ptrEqual(p1, p2)) 134 | return 0; 135 | 136 | bool done = false; 137 | 138 | #ifdef VIRTMEM_WRAP_CPOINTERS 139 | if (!TVirtPtrTraits::isVirtPtr() && !TVirtPtrTraits::isVirtPtr()) 140 | { 141 | // Even though both are pointers, we must unwrap to avoid compile errors with other types 142 | return comparator(TVirtPtrTraits::unwrap(p1), TVirtPtrTraits::unwrap(p2), n, done); 143 | } 144 | else if (TVirtPtrTraits::isWrapped(p1) && TVirtPtrTraits::isWrapped(p2)) 145 | return comparator(TVirtPtrTraits::unwrap(p1), TVirtPtrTraits::unwrap(p2), n, done); 146 | else if (TVirtPtrTraits::isWrapped(p1)) 147 | return rawCompare(TVirtPtrTraits::unwrap(p1), p2, n, comparator); 148 | else if (TVirtPtrTraits::isWrapped(p2)) 149 | return rawCompare(p1, TVirtPtrTraits::unwrap(p2), n, comparator); 150 | #endif 151 | 152 | VPtrSize sizeleft = n; 153 | const VirtPageSize maxlocksize = getMaxLockSize(p1, p2); 154 | 155 | while (sizeleft) 156 | { 157 | VirtPageSize cmpsize = minimal(static_cast(maxlocksize), sizeleft); 158 | typename TVirtPtrTraits::Lock l1 = TVirtPtrTraits::makeLock(p1, cmpsize, true); 159 | cmpsize = minimal(cmpsize, TVirtPtrTraits::getLockSize(l1)); 160 | typename TVirtPtrTraits::Lock l2 = TVirtPtrTraits::makeLock(p2, cmpsize, true); 161 | cmpsize = minimal(cmpsize, TVirtPtrTraits::getLockSize(l2)); 162 | 163 | int cmp = comparator(*l1, *l2, cmpsize, done); 164 | if (cmp != 0 || done) 165 | return cmp; 166 | 167 | p1 += cmpsize; p2 += cmpsize; 168 | sizeleft -= cmpsize; 169 | ASSERT(sizeleft <= n); 170 | } 171 | 172 | return 0; 173 | } 174 | 175 | inline bool memCopier(char *dest, const char *src, VPtrSize n) 176 | { 177 | memcpy(dest, src, n); 178 | return true; 179 | } 180 | 181 | inline bool strnCopier(char *dest, const char *src, VPtrSize n) 182 | { 183 | return n && (strncpy(dest, src, n))[n-1] != 0; // strncpy pads with zero 184 | } 185 | 186 | inline bool strCopier(char *dest, const char *src, VPtrSize n) 187 | { 188 | // can't use strcpy since it doesn't take into account specified size, cannot take 189 | // strncpy either since it pads and since we don't actually know size of dest 190 | // it could pad too much... well could you just use something else then strcpy? 191 | 192 | if (n == 0) 193 | return false; 194 | 195 | char *d = dest; 196 | const char *s = src; 197 | for (; n; --n, ++d, ++s) 198 | { 199 | *d = *s; 200 | if (*d == 0) 201 | return false; // abort if 0 found 202 | } 203 | 204 | return true; 205 | } 206 | 207 | inline int memComparator(const char *p1, const char *p2, VPtrSize n, bool &) 208 | { return ::memcmp(p1, p2, n); } 209 | inline int strnComparator(const char *p1, const char *p2, VPtrSize n, bool &) 210 | { return ::strncmp(p1, p2, n); } 211 | inline int strComparator(const char *p1, const char *p2, VPtrSize n, bool &done) 212 | { 213 | const int ret = ::strncmp(p1, p2, n); 214 | if (ret == 0) 215 | { 216 | // did we encounter a zero? 217 | for (; n && *p1; --n, ++p1) 218 | ; 219 | done = (n != 0); // we're done if there was a string terminator 220 | } 221 | return ret; 222 | } 223 | 224 | } 225 | 226 | 227 | // Specialize memcpy/memset/memcmp for char types: we may have to convert to this type anyway, 228 | // and this saves some template code duplication for other types. A general template function will just 229 | // cast and call the specialized function. 230 | 231 | 232 | /** 233 | * @anchor Coverloads 234 | * @name Overloads of C library functions for virtual pointers 235 | * The following functions are overloads of some common C functions for dealing with memory and strings. 236 | * They accept virtual pointers or a mix of virtual and regular pointers. Please note that they are 237 | * defined in the [virtmem namespace](@ref virtmem) like any other code from `virtmem`, hence, they will not 238 | * "polute" the global namespace unless you want to (i.e. by using the `using` directive). 239 | * @{ 240 | **/ 241 | 242 | template 243 | VPtr memcpy(VPtr dest, const VPtr src, VPtrSize size) 244 | { 245 | return static_cast >( 246 | private_utils::rawCopy(static_cast >(dest), 247 | static_cast >(src), size, 248 | private_utils::memCopier)); 249 | } 250 | 251 | template VPtr memcpy(VPtr dest, const void *src, VPtrSize size) 252 | { 253 | return static_cast >( 254 | private_utils::rawCopy(static_cast >(dest), 255 | static_cast(src), size, 256 | private_utils::memCopier)); 257 | } 258 | 259 | template void *memcpy(void *dest, VPtr src, VPtrSize size) 260 | { 261 | return private_utils::rawCopy(static_cast(dest), 262 | static_cast >(src), size, 263 | private_utils::memCopier); 264 | } 265 | 266 | template VPtr memset(VPtr dest, int c, VPtrSize size) 267 | { 268 | if (size == 0) 269 | return dest; 270 | 271 | #ifdef VIRTMEM_WRAP_CPOINTERS 272 | if (dest.isWrapped()) 273 | { 274 | ::memset(dest.unwrap(), c, size); 275 | return dest; 276 | } 277 | #endif 278 | 279 | VPtrSize sizeleft = size; 280 | VPtr p = dest; 281 | 282 | while (sizeleft) 283 | { 284 | VirtPageSize setsize = private_utils::minimal((VPtrSize)A::getInstance()->getBigPageSize(), sizeleft); 285 | VPtrLock > l = makeVirtPtrLock(p, setsize); 286 | setsize = l.getLockSize(); 287 | ::memset(*l, c, setsize); 288 | p += setsize; sizeleft -= setsize; 289 | } 290 | 291 | return dest; 292 | } 293 | 294 | template VPtr memset(VPtr dest, int c, VPtrSize size) 295 | { 296 | return static_cast >(memset(static_cast >(dest), c, size)); 297 | } 298 | 299 | template int memcmp(VPtr s1, const VPtr s2, 300 | VPtrSize n) 301 | { 302 | #ifdef VIRTMEM_WRAP_CPOINTERS 303 | if (s1.isWrapped() && s2.isWrapped()) 304 | return ::memcmp(s1.unwrap(), s2.unwrap(), n); 305 | #endif 306 | 307 | return private_utils::rawCompare(static_cast >(s1), 308 | static_cast >(s2), n, private_utils::memComparator); 309 | } 310 | 311 | template int memcmp(VPtr s1, const void *s2, VPtrSize n) 312 | { 313 | return private_utils::rawCompare(static_cast >(s1), 314 | static_cast(s2), n, private_utils::memComparator); 315 | } 316 | 317 | template int memcmp(const void *s1, const VPtr s2, VPtrSize n) 318 | { 319 | return private_utils::rawCompare(static_cast(s1), 320 | static_cast >(s2), n, private_utils::memComparator); 321 | } 322 | 323 | template VPtr strncpy(VPtr dest, const VPtr src, 324 | VPtrSize n) 325 | { 326 | return private_utils::rawCopy(dest, src, n, private_utils::strnCopier); 327 | } 328 | 329 | template VPtr strncpy(VPtr dest, const char *src, VPtrSize n) 330 | { 331 | return private_utils::rawCopy(dest, src, n, private_utils::strnCopier); 332 | } 333 | 334 | template char *strncpy(char *dest, const VPtr src, VPtrSize n) 335 | { 336 | return private_utils::rawCopy(dest, src, n, private_utils::strnCopier); 337 | } 338 | 339 | template VPtr strcpy(VPtr dest, const VPtr src) 340 | { 341 | return private_utils::rawCopy(dest, src, (VPtrSize)-1, private_utils::strCopier); 342 | } 343 | 344 | template VPtr strcpy(VPtr dest, const char *src) 345 | { 346 | return private_utils::rawCopy(dest, src, (VPtrSize)-1, private_utils::strCopier); 347 | } 348 | 349 | template char *strcpy(char *dest, const VPtr src) 350 | { 351 | return private_utils::rawCopy(dest, src, (VPtrSize)-1, private_utils::strCopier); 352 | } 353 | 354 | template int strncmp(VPtr dest, VPtr src, VPtrSize n) 355 | { return private_utils::rawCompare(dest, src, n, private_utils::strnComparator);} 356 | template int strncmp(VPtr dest, const char *src, VPtrSize n) 357 | { return private_utils::rawCompare(dest, src, n, private_utils::strnComparator); } 358 | template int strncmp(const char *dest, VPtr src, VPtrSize n) 359 | { return private_utils::rawCompare(dest, src, n, private_utils::strnComparator); } 360 | 361 | template int strcmp(VPtr dest, VPtr src) 362 | { return private_utils::rawCompare(dest, src, (VPtrSize)-1, private_utils::strComparator); } 363 | template int strcmp(const char *dest, VPtr src) 364 | { return private_utils::rawCompare(dest, src, (VPtrSize)-1, private_utils::strComparator); } 365 | template int strcmp(VPtr dest, const char *src) 366 | { return private_utils::rawCompare(dest, src, (VPtrSize)-1, private_utils::strComparator); } 367 | 368 | template int strlen(VPtr str) 369 | { 370 | #ifdef VIRTMEM_WRAP_CPOINTERS 371 | if (str.isWrapped()) 372 | return ::strlen(str.unwrap()); 373 | #endif 374 | 375 | int ret = 0; 376 | for (; *str != 0; ++str) 377 | ++ret; 378 | return ret; 379 | } 380 | 381 | // const <--> non const madness 382 | // ----- 383 | 384 | template VPtr strncpy(VPtr dest, const VPtr src, 385 | VPtrSize n) 386 | { return strncpy(dest, static_cast >(src), n); } 387 | template char *strncpy(char *dest, const VPtr src, VPtrSize n) 388 | { return strncpy(dest, static_cast >(src), n); } 389 | 390 | template VPtr strcpy(VPtr dest, const VPtr src) 391 | { return strcpy(dest, static_cast >(src)); } 392 | template char *strcpy(char *dest, const VPtr src) 393 | { return strcpy(dest, static_cast >(src)); } 394 | 395 | template int strncmp(VPtr dest, VPtr src, VPtrSize n) 396 | { return strncmp(static_cast >(dest), static_cast >(src), n); } 397 | template int strncmp(VPtr dest, VPtr src, VPtrSize n) 398 | { return strncmp(dest, static_cast >(src), n); } 399 | template int strncmp(VPtr dest, VPtr src, VPtrSize n) 400 | { return strncmp(static_cast >(dest), src, n); } 401 | template int strncmp(const char *dest, VPtr src, VPtrSize n) 402 | { return strncmp(dest, static_cast >(src), n); } 403 | template int strncmp(VPtr dest, const char *src, VPtrSize n) 404 | { return strncmp(static_cast >(dest), src, n); } 405 | 406 | template int strcmp(VPtr dest, VPtr src) 407 | { return strcmp(static_cast >(dest), static_cast >(src)); } 408 | template int strcmp(VPtr dest, VPtr src) 409 | { return strcmp(dest, static_cast >(src)); } 410 | template int strcmp(VPtr dest, VPtr src) 411 | { return strcmp(static_cast >(dest), src); } 412 | template int strcmp(const char *dest, VPtr src) 413 | { return strcmp(dest, static_cast >(src)); } 414 | template int strcmp(VPtr dest, const char *src) 415 | { return strcmp(static_cast >(dest), src); } 416 | 417 | template int strlen(VPtr str) { return strlen(static_cast >(str)); } 418 | 419 | // @} 420 | 421 | } 422 | 423 | #endif // VIRTMEM_UTILS_HPP 424 | --------------------------------------------------------------------------------