├── 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 | | Read / Write speed (kB/s)
82 | | Teensy 3.2 (96 MHz)
83 | | Teensy 3.2 (144 MHz)
84 | | Arduino Uno
85 | |
86 | | Serial
87 | | 500 / 373
88 | | 496 / 378
89 | | 30 / 20
90 | |
91 | | SD
92 | | 1107 / 98
93 | | 1102 / 91
94 | | 156 / 44
95 | |
96 | | 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