Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define _CRT_SECURE_NO_DEPRECATE
- #include <stdio.h>
- #include <string.h>
- #include <Windows.h>
- // This allocates a "magic ring buffer" that is mapped twice, with the two
- // copies being contiguous in (virtual) memory. The advantage of this is
- // that this allows any function that expects data to be contiguous in
- // memory to read from (or write to) such a buffer. It also means that
- // block reads/writes never need to be split into two halves, and makes
- // wraparound handling quite cheap.
- //
- // The flipside is that allocating such a beast is a bit dicey (see
- // comments below) and subject to various restrictions (e.g. on Windows,
- // the size of such a buffer must be a multiple of 64k). So the usual
- // disclaimer applies: code responsibly, with great power comes great
- // responsibility, and when you use this code to shoot yourself in the
- // foot it might blow off your face instead (what with the wraparound
- // and all).
- class MagicRingBuffer
- {
- HANDLE mapping;
- size_t size;
- char *baseptr;
- public:
- MagicRingBuffer()
- : mapping(0), size(0), baseptr(0)
- {
- }
- ~MagicRingBuffer()
- {
- free();
- }
- // Allocate a magic ring buffer at a given target address.
- // ring_size size of one copy of the ring; must be a multiple of 64k.
- // desired_addr location where you'd like it.
- void *alloc_at(size_t ring_size, void *desired_addr=0)
- {
- // if we already hold one allocation, refuse to make another.
- if (baseptr)
- return 0;
- // is ring_size a multiple of 64k? if not, this won't ever work!
- if ((ring_size & 0xffff) != 0)
- return 0;
- // try to allocate and map our space
- size_t alloc_size = ring_size * 2;
- if (!(mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, (unsigned long long)alloc_size >> 32, alloc_size & 0xffffffffu, 0)) ||
- !(baseptr = (char *)MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0, ring_size, desired_addr)) ||
- !MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0, ring_size, (char *)desired_addr + ring_size))
- {
- // something went wrong - clean up
- free();
- }
- else // success!
- size = ring_size;
- return baseptr;
- }
- // This function will allocate a magic ring buffer at a system-determined base address.
- //
- // Sadly, there's no way (that I can see) in the Win32 API to first reserve
- // a memory region then fill it in using mmaps; you can reserve memory via
- // VirtualAlloc, but that address range can then only be used to commit
- // memory via another VirtualAlloc, and can not be mmap'ed. Furthermore,
- // there's also no way to do the two back-to-back mmaps atomically. What we
- // do here is to reserve enough memory via VirtualAlloc, then immediately
- // free it and try to put our allocation there. This is subject to a race
- // condition - another thread might end up allocating that very memory
- // region in the interim. What this means is that even when an alloc should
- // work (i.e. there's enough memory available) it can still fail spuriously
- // sometimes. Hence the "dicey" comment above.
- //
- // What we do here is just retry the alloc a given number of times and hope
- // that we don't get screwed every single time. This increases the
- // likelihood of success, but doesn't eliminate the chance of spurious
- // failure, so be religious about checking return values!
- void *alloc(size_t ring_size, int num_retries=5)
- {
- void *ptr = 0;
- while (!ptr && num_retries-- != 0)
- {
- void *target_addr = determine_viable_addr(ring_size * 2);
- if (target_addr)
- ptr = alloc_at(ring_size, target_addr);
- }
- return ptr;
- }
- // Frees the allocated region again.
- void free()
- {
- if (baseptr)
- {
- UnmapViewOfFile(baseptr);
- UnmapViewOfFile(baseptr + size);
- baseptr = 0;
- }
- if (mapping)
- {
- CloseHandle(mapping);
- mapping = 0;
- }
- size = 0;
- }
- private:
- // Determine a viable target address of "size" memory mapped bytes by
- // allocating memory using VirtualAlloc and immediately freeing it. This
- // is subject to a potential race condition, see notes above.
- static void *determine_viable_addr(size_t size)
- {
- void *ptr = VirtualAlloc(0, size, MEM_RESERVE, PAGE_NOACCESS);
- if (!ptr)
- return 0;
- VirtualFree(ptr, 0, MEM_RELEASE);
- return ptr;
- }
- };
- int main()
- {
- static const int ringsize = 64*1024;
- MagicRingBuffer mrb;
- char *buf = (char *)mrb.alloc(ringsize);
- if (!buf)
- {
- printf("MRB allocation failed!\n");
- return 0;
- }
- // Okay, now get ready for a magic trick!
- memset(buf, 0, ringsize * 2); // clear it all to zeroes
- strcpy(buf + ringsize - 3, "Hello world!");
- printf("%s\n", buf + ringsize - 3);
- printf("%s\n", buf);
- // nothing up my sleeve!
- return 0;
- }
Add Comment
Please, Sign In to add comment