2010-08-09 13:28:56 +00:00
|
|
|
#ifndef NALL_VECTOR_HPP
|
|
|
|
#define NALL_VECTOR_HPP
|
|
|
|
|
2011-09-27 11:55:02 +00:00
|
|
|
#include <algorithm>
|
2010-08-09 13:28:56 +00:00
|
|
|
#include <initializer_list>
|
|
|
|
#include <new>
|
|
|
|
#include <type_traits>
|
|
|
|
#include <utility>
|
|
|
|
#include <nall/algorithm.hpp>
|
|
|
|
#include <nall/bit.hpp>
|
2012-01-26 06:50:09 +00:00
|
|
|
#include <nall/sort.hpp>
|
2010-08-09 13:28:56 +00:00
|
|
|
#include <nall/utility.hpp>
|
|
|
|
|
|
|
|
namespace nall {
|
2011-10-16 09:44:48 +00:00
|
|
|
template<typename T> struct vector {
|
|
|
|
struct exception_out_of_bounds{};
|
|
|
|
|
|
|
|
protected:
|
|
|
|
T *pool;
|
|
|
|
unsigned poolsize;
|
|
|
|
unsigned objectsize;
|
|
|
|
|
|
|
|
public:
|
Update to v088r02 release.
byuu says:
Basically, the current implementation of nall/array is deprecated now.
The old method was for non-reference types, it acted like a vector for
POD types (raw memory allocation instead of placement new construction.)
And for reference types, it acted like an unordered set. Yeah, not good.
As of right now, nall/array is no longer used. The vector type usage was
replaced with actual vectors.
I've created nall/set, which now contains the specialization for
reference types.
nall/set basically acts much like std::unordered_set. No auto-sort, only
one of each type is allowed, automatic growth.
This will be the same both for reference and non-reference types ...
however, the non-reference type wasn't implemented just yet.
Future plans for nall/array are for it to be a statically allocated
block of memory, ala array<type, size>, which is meant for RAII memory
usage.
Have to work on the specifics, eg the size as a template parameter may
be problematic. I'd like to return allocated chunks of memory (eg
file::read) in this container so that I don't have to manually free the
data anymore.
I also removed nall/moduloarray, and moved that into the SNES DSP class,
since that's the only thing that uses it.
2012-04-26 10:56:15 +00:00
|
|
|
T* data() { return pool; }
|
2011-10-16 09:44:48 +00:00
|
|
|
unsigned size() const { return objectsize; }
|
|
|
|
unsigned capacity() const { return poolsize; }
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
if(pool) {
|
|
|
|
for(unsigned n = 0; n < objectsize; n++) pool[n].~T();
|
|
|
|
free(pool);
|
|
|
|
}
|
|
|
|
pool = nullptr;
|
|
|
|
poolsize = 0;
|
|
|
|
objectsize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reserve(unsigned size) {
|
|
|
|
size = bit::round(size); //amortize growth
|
|
|
|
T *copy = (T*)calloc(size, sizeof(T));
|
|
|
|
for(unsigned n = 0; n < min(size, objectsize); n++) new(copy + n) T(pool[n]);
|
|
|
|
for(unsigned n = 0; n < objectsize; n++) pool[n].~T();
|
|
|
|
free(pool);
|
|
|
|
pool = copy;
|
|
|
|
poolsize = size;
|
|
|
|
objectsize = min(size, objectsize);
|
|
|
|
}
|
|
|
|
|
Update to v088r02 release.
byuu says:
Basically, the current implementation of nall/array is deprecated now.
The old method was for non-reference types, it acted like a vector for
POD types (raw memory allocation instead of placement new construction.)
And for reference types, it acted like an unordered set. Yeah, not good.
As of right now, nall/array is no longer used. The vector type usage was
replaced with actual vectors.
I've created nall/set, which now contains the specialization for
reference types.
nall/set basically acts much like std::unordered_set. No auto-sort, only
one of each type is allowed, automatic growth.
This will be the same both for reference and non-reference types ...
however, the non-reference type wasn't implemented just yet.
Future plans for nall/array are for it to be a statically allocated
block of memory, ala array<type, size>, which is meant for RAII memory
usage.
Have to work on the specifics, eg the size as a template parameter may
be problematic. I'd like to return allocated chunks of memory (eg
file::read) in this container so that I don't have to manually free the
data anymore.
I also removed nall/moduloarray, and moved that into the SNES DSP class,
since that's the only thing that uses it.
2012-04-26 10:56:15 +00:00
|
|
|
//requires trivial constructor
|
|
|
|
void resize(unsigned size) {
|
|
|
|
if(size == objectsize) return;
|
|
|
|
if(size < objectsize) return reserve(size);
|
|
|
|
while(size > objectsize) append(T());
|
|
|
|
}
|
|
|
|
|
2011-10-16 09:44:48 +00:00
|
|
|
template<typename... Args>
|
|
|
|
void append(const T& data, Args&&... args) {
|
|
|
|
append(data);
|
|
|
|
append(std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
void append(const T& data) {
|
|
|
|
if(objectsize + 1 > poolsize) reserve(objectsize + 1);
|
|
|
|
new(pool + objectsize++) T(data);
|
|
|
|
}
|
|
|
|
|
Update to v087r30 release.
byuu says:
Changelog:
- DMA channel masks added (some are 27-bit source/target and some are
14-bit length -- hooray, varuint_t class.)
- No more state.pending flags. Instead, we set dma.pending flag when we
want a transfer (fixes GBA Video - Pokemon audio) [Cydrak]
- fixed OBJ Vmosaic [Cydrak, krom]
- OBJ cannot read <=0x13fff in BG modes 3-5 (fixes the garbled tile at
the top-left of some games)
- DMA timing should be much closer to hardware now, but probably not
perfect
- PPU frame blending uses blargg's bit-perfect, rounded method (slower,
but what can you do?)
- GBA carts really unload now
- added nall/gba/cartridge.hpp: used when there is no manifest. Scans
ROMs for library tags, and selects the first valid one found
- added EEPROM auto-detection when EEPROM size=0. Forces disk/save state
size to 8192 (otherwise states could crash between pre and post
detect.)
- detects first read after a set read address command when the size
is zero, and sets all subsequent bit-lengths to that value, prints
detected size to terminal
- added nall/nes/cartridge.hpp: moves iNES detection out of emulation
core.
Important to note: long-term goal is to remove all
nall/(system)/cartridge.hpp detections from the core and replace with
databases. All in good time.
Anyway, the GBA workarounds should work for ~98.5% of the library, if my
pre-scanning was correct (~40 games with odd tags. I reject ones without
numeric versions now, too.)
I think we're basically at a point where we can release a new version
now. Compatibility should be relatively high (at least for a first
release), and fixes are only going to affect one or two games at a time.
I'd like to start doing some major cleaning house internally (rename
NES->Famicom, SNES->SuperFamicom and such.) Would be much wiser to do
that on a .01 WIP to minimize regressions.
The main problems with a release now:
- speed is pretty bad, haven't really optimized much yet (not sure how
much we can improve it yet, this usually isn't easy)
- sound isn't -great-, but the GBA audio sucks anyway :P
- couple of known bugs (Sonic X video, etc.)
2012-04-22 10:49:19 +00:00
|
|
|
bool appendonce(const T& data) {
|
|
|
|
if(find(data) == true) return false;
|
|
|
|
append(data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-01-15 08:29:57 +00:00
|
|
|
void insert(unsigned position, const T& data) {
|
Update to v084r01 release.
I rewrote the S-SMP processor core (implementation of the 256 opcodes),
utilizing my new 6502-like syntax. It matches what bass v05r01 uses.
Took 10 hours.
Due to being able to group the "mov reg,mem" opcodes together with
"adc/sbc/ora/and/eor/cmp" sets, the total code size was reduced from
55.7KB to 42.5KB for identical accuracy and speed.
I also dropped the trick I was using to pass register variables as
template arguments, and instead just use a switch table to pass them as
function arguments. Makes the table a lot easier to read.
Passes all of my S-SMP tests, and all of blargg's
arithmetic/cycle-timing S-SMP tests. Runs Zelda 3 great as well. Didn't
test further.
This does have the potential to cause some regressions if I've messed
anything up, and none of the above tests caught it, so as always,
testing would be appreciated.
Anyway, yeah. By writing the actual processor with this new mnemonic
set, it confirms the parallels I've made.
My guess is that Sony really did clone the 6502, but was worried about
legal implications or something and changed the mnemonics last-minute.
(Note to self: need to re-enable snes.random before v085 official.)
EDIT: oh yeah, I also commented out the ALSA snd_pcm_drain() inside
term(). Without it, there is a tiny pop when the driver is
re-initialized. But with it, the entire emulator would lock up for five
whole seconds waiting on that call to complete. I'll take the pop any
day over that.
2011-11-17 12:05:35 +00:00
|
|
|
append(data);
|
2012-01-15 08:29:57 +00:00
|
|
|
for(signed n = size() - 1; n > position; n--) pool[n] = pool[n - 1];
|
|
|
|
pool[position] = data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void prepend(const T& data) {
|
|
|
|
insert(0, data);
|
Update to v084r01 release.
I rewrote the S-SMP processor core (implementation of the 256 opcodes),
utilizing my new 6502-like syntax. It matches what bass v05r01 uses.
Took 10 hours.
Due to being able to group the "mov reg,mem" opcodes together with
"adc/sbc/ora/and/eor/cmp" sets, the total code size was reduced from
55.7KB to 42.5KB for identical accuracy and speed.
I also dropped the trick I was using to pass register variables as
template arguments, and instead just use a switch table to pass them as
function arguments. Makes the table a lot easier to read.
Passes all of my S-SMP tests, and all of blargg's
arithmetic/cycle-timing S-SMP tests. Runs Zelda 3 great as well. Didn't
test further.
This does have the potential to cause some regressions if I've messed
anything up, and none of the above tests caught it, so as always,
testing would be appreciated.
Anyway, yeah. By writing the actual processor with this new mnemonic
set, it confirms the parallels I've made.
My guess is that Sony really did clone the 6502, but was worried about
legal implications or something and changed the mnemonics last-minute.
(Note to self: need to re-enable snes.random before v085 official.)
EDIT: oh yeah, I also commented out the ALSA snd_pcm_drain() inside
term(). Without it, there is a tiny pop when the driver is
re-initialized. But with it, the entire emulator would lock up for five
whole seconds waiting on that call to complete. I'll take the pop any
day over that.
2011-11-17 12:05:35 +00:00
|
|
|
}
|
|
|
|
|
Update to v086r16 release.
byuu says:
Cydrak, I moved the step from the opcode decoder and opcodes themselves
into bus_(read,write)(byte,word), to minimize code.
If that's not feasible for some reason, please let me know and I'll
change it back to your latest WIP.
This has your carry flag fix, the timer skeleton (doesn't really work
yet), the Booth two-bit steps, and the carry flag clear thing inside
multiply ops.
Also added the aforementioned reset delay and reset bit stuff, and fixed
the steps to 21MHz for instructions and 64KHz for reset pulse.
I wasn't sure about the shifter extra cycles. I only saw it inside one
of the four (or was it three?) opcodes that have shifter functions.
Shouldn't it be in all of them?
The game does indeed appear to be fully playable now, but the AI doesn't
exactly match my real cartridge.
This could be for any number of reasons: ARM CPU bug, timer behavior
bug, oscillator differences between my real hardware and the emulator,
etc.
However ... the AI is 100% predictable every time, both under emulation
and on real hardware.
- For the first step, move 九-1 to 八-1.
- The opponent moves 三-3 to 四-3.
- Now move 七-1 to 六-1.
- The opponent moves 二-2 to 八-8.
However, on my real SNES, the opponent moves 一-3 to 二-4.
2012-03-07 13:03:15 +00:00
|
|
|
void remove(unsigned index = ~0u, unsigned count = 1) {
|
|
|
|
if(index == ~0) index = objectsize ? objectsize - 1 : 0;
|
2012-01-15 08:29:57 +00:00
|
|
|
for(unsigned n = index; count + n < objectsize; n++) pool[n] = pool[count + n];
|
2011-10-16 09:44:48 +00:00
|
|
|
objectsize = (count + index >= objectsize) ? index : objectsize - count;
|
|
|
|
}
|
|
|
|
|
Update to v086r16 release.
byuu says:
Cydrak, I moved the step from the opcode decoder and opcodes themselves
into bus_(read,write)(byte,word), to minimize code.
If that's not feasible for some reason, please let me know and I'll
change it back to your latest WIP.
This has your carry flag fix, the timer skeleton (doesn't really work
yet), the Booth two-bit steps, and the carry flag clear thing inside
multiply ops.
Also added the aforementioned reset delay and reset bit stuff, and fixed
the steps to 21MHz for instructions and 64KHz for reset pulse.
I wasn't sure about the shifter extra cycles. I only saw it inside one
of the four (or was it three?) opcodes that have shifter functions.
Shouldn't it be in all of them?
The game does indeed appear to be fully playable now, but the AI doesn't
exactly match my real cartridge.
This could be for any number of reasons: ARM CPU bug, timer behavior
bug, oscillator differences between my real hardware and the emulator,
etc.
However ... the AI is 100% predictable every time, both under emulation
and on real hardware.
- For the first step, move 九-1 to 八-1.
- The opponent moves 三-3 to 四-3.
- Now move 七-1 to 六-1.
- The opponent moves 二-2 to 八-8.
However, on my real SNES, the opponent moves 一-3 to 二-4.
2012-03-07 13:03:15 +00:00
|
|
|
T take(unsigned index = ~0u) {
|
|
|
|
if(index == ~0) index = objectsize ? objectsize - 1 : 0;
|
|
|
|
if(index >= objectsize) throw exception_out_of_bounds();
|
|
|
|
T item = pool[index];
|
|
|
|
remove(index);
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2012-01-26 06:50:09 +00:00
|
|
|
void sort() {
|
|
|
|
nall::sort(pool, objectsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Comparator> void sort(const Comparator &lessthan) {
|
|
|
|
nall::sort(pool, objectsize, lessthan);
|
|
|
|
}
|
|
|
|
|
|
|
|
optional<unsigned> find(const T& data) {
|
|
|
|
for(unsigned n = 0; n < size(); n++) if(pool[n] == data) return { true, n };
|
|
|
|
return { false, 0u };
|
|
|
|
}
|
|
|
|
|
Update to v086r16 release.
byuu says:
Cydrak, I moved the step from the opcode decoder and opcodes themselves
into bus_(read,write)(byte,word), to minimize code.
If that's not feasible for some reason, please let me know and I'll
change it back to your latest WIP.
This has your carry flag fix, the timer skeleton (doesn't really work
yet), the Booth two-bit steps, and the carry flag clear thing inside
multiply ops.
Also added the aforementioned reset delay and reset bit stuff, and fixed
the steps to 21MHz for instructions and 64KHz for reset pulse.
I wasn't sure about the shifter extra cycles. I only saw it inside one
of the four (or was it three?) opcodes that have shifter functions.
Shouldn't it be in all of them?
The game does indeed appear to be fully playable now, but the AI doesn't
exactly match my real cartridge.
This could be for any number of reasons: ARM CPU bug, timer behavior
bug, oscillator differences between my real hardware and the emulator,
etc.
However ... the AI is 100% predictable every time, both under emulation
and on real hardware.
- For the first step, move 九-1 to 八-1.
- The opponent moves 三-3 to 四-3.
- Now move 七-1 to 六-1.
- The opponent moves 二-2 to 八-8.
However, on my real SNES, the opponent moves 一-3 to 二-4.
2012-03-07 13:03:15 +00:00
|
|
|
T& first() {
|
|
|
|
if(objectsize == 0) throw exception_out_of_bounds();
|
|
|
|
return pool[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
T& last() {
|
|
|
|
if(objectsize == 0) throw exception_out_of_bounds();
|
|
|
|
return pool[objectsize - 1];
|
|
|
|
}
|
|
|
|
|
2011-10-16 09:44:48 +00:00
|
|
|
//access
|
|
|
|
inline T& operator[](unsigned position) {
|
|
|
|
if(position >= objectsize) throw exception_out_of_bounds();
|
|
|
|
return pool[position];
|
|
|
|
}
|
|
|
|
|
|
|
|
inline const T& operator[](unsigned position) const {
|
|
|
|
if(position >= objectsize) throw exception_out_of_bounds();
|
|
|
|
return pool[position];
|
|
|
|
}
|
|
|
|
|
2012-01-26 06:50:09 +00:00
|
|
|
inline T& operator()(unsigned position) {
|
|
|
|
if(position >= poolsize) reserve(position + 1);
|
|
|
|
while(position >= objectsize) append(T());
|
|
|
|
return pool[position];
|
|
|
|
}
|
|
|
|
|
2011-10-16 09:44:48 +00:00
|
|
|
inline const T& operator()(unsigned position, const T& data) const {
|
|
|
|
if(position >= objectsize) return data;
|
|
|
|
return pool[position];
|
|
|
|
}
|
|
|
|
|
|
|
|
//iteration
|
|
|
|
T* begin() { return &pool[0]; }
|
|
|
|
T* end() { return &pool[objectsize]; }
|
|
|
|
const T* begin() const { return &pool[0]; }
|
|
|
|
const T* end() const { return &pool[objectsize]; }
|
|
|
|
|
|
|
|
//copy
|
|
|
|
inline vector& operator=(const vector &source) {
|
|
|
|
reset();
|
|
|
|
reserve(source.capacity());
|
|
|
|
for(auto &data : source) append(data);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector(const vector &source) : pool(nullptr), poolsize(0), objectsize(0) {
|
|
|
|
operator=(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
//move
|
|
|
|
inline vector& operator=(vector &&source) {
|
|
|
|
reset();
|
|
|
|
pool = source.pool, poolsize = source.poolsize, objectsize = source.objectsize;
|
|
|
|
source.pool = nullptr, source.poolsize = 0, source.objectsize = 0;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector(vector &&source) : pool(nullptr), poolsize(0), objectsize(0) {
|
|
|
|
operator=(std::move(source));
|
|
|
|
}
|
|
|
|
|
|
|
|
//construction
|
|
|
|
vector() : pool(nullptr), poolsize(0), objectsize(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
vector(std::initializer_list<T> list) : pool(nullptr), poolsize(0), objectsize(0) {
|
|
|
|
for(auto &data : list) append(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
~vector() {
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-08-09 13:28:56 +00:00
|
|
|
//linear_vector
|
|
|
|
//memory: O(capacity * 2)
|
|
|
|
//
|
|
|
|
//linear_vector uses placement new + manual destructor calls to create a
|
|
|
|
//contiguous block of memory for all objects. accessing individual elements
|
|
|
|
//is fast, though resizing the array incurs significant overhead.
|
|
|
|
//reserve() overhead is reduced from quadratic time to amortized constant time
|
|
|
|
//by resizing twice as much as requested.
|
|
|
|
//
|
|
|
|
//if objects hold memory address references to themselves (introspection), a
|
|
|
|
//valid copy constructor will be needed to keep pointers valid.
|
|
|
|
|
2012-01-26 06:50:09 +00:00
|
|
|
#define NALL_DEPRECATED
|
|
|
|
#if defined(NALL_DEPRECATED)
|
2011-10-16 09:44:48 +00:00
|
|
|
template<typename T> struct linear_vector {
|
2010-08-09 13:28:56 +00:00
|
|
|
protected:
|
|
|
|
T *pool;
|
|
|
|
unsigned poolsize, objectsize;
|
|
|
|
|
|
|
|
public:
|
|
|
|
unsigned size() const { return objectsize; }
|
|
|
|
unsigned capacity() const { return poolsize; }
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
if(pool) {
|
|
|
|
for(unsigned i = 0; i < objectsize; i++) pool[i].~T();
|
|
|
|
free(pool);
|
|
|
|
}
|
2011-09-29 12:08:22 +00:00
|
|
|
pool = nullptr;
|
2010-08-09 13:28:56 +00:00
|
|
|
poolsize = 0;
|
|
|
|
objectsize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reserve(unsigned newsize) {
|
|
|
|
newsize = bit::round(newsize); //round to nearest power of two (for amortized growth)
|
|
|
|
|
Update to v074r01 release.
byuu says:
Changelog:
- fixed libsnes to compile again, the GB RTC constant is there but
doesn't do anything just yet (no serialize support in GameBoy core)
- libsnes: added SNES_MEMORY_(WRAM,APURAM,VRAM,OAM,CGRAM) -- really only
for the first one, it allows libsnes users to implement their own
cheat search
- you can now load the SGB BIOS without a game!! please be sure to enjoy
the blinking cartridge icon emulation :D
- necdsp (uPD7725,96050) - simplified code a bit: removed persistent
regs.idb, simplified jumps, merged exec() with main loop, etc.
- nall::function - fixed an initialization bug when copy-constructing
objects
- nall::vector - use calloc instead of malloc to help safeguard against
uninitialized class data (potentially hides errors, but better than
crashing in production)
2011-01-13 10:07:04 +00:00
|
|
|
T *poolcopy = (T*)calloc(newsize, sizeof(T));
|
2010-08-09 13:28:56 +00:00
|
|
|
for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]);
|
|
|
|
for(unsigned i = 0; i < objectsize; i++) pool[i].~T();
|
|
|
|
free(pool);
|
|
|
|
pool = poolcopy;
|
|
|
|
poolsize = newsize;
|
|
|
|
objectsize = min(objectsize, newsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void resize(unsigned newsize) {
|
|
|
|
if(newsize > poolsize) reserve(newsize);
|
|
|
|
|
|
|
|
if(newsize < objectsize) {
|
|
|
|
//vector is shrinking; destroy excess objects
|
|
|
|
for(unsigned i = newsize; i < objectsize; i++) pool[i].~T();
|
|
|
|
} else if(newsize > objectsize) {
|
|
|
|
//vector is expanding; allocate new objects
|
|
|
|
for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T;
|
|
|
|
}
|
|
|
|
|
|
|
|
objectsize = newsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void append(const T data) {
|
|
|
|
if(objectsize + 1 > poolsize) reserve(objectsize + 1);
|
|
|
|
new(pool + objectsize++) T(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename U> void insert(unsigned index, const U list) {
|
|
|
|
linear_vector<T> merged;
|
|
|
|
for(unsigned i = 0; i < index; i++) merged.append(pool[i]);
|
2011-09-27 11:55:02 +00:00
|
|
|
for(auto &item : list) merged.append(item);
|
2010-08-09 13:28:56 +00:00
|
|
|
for(unsigned i = index; i < objectsize; i++) merged.append(pool[i]);
|
|
|
|
operator=(merged);
|
|
|
|
}
|
|
|
|
|
|
|
|
void insert(unsigned index, const T item) {
|
|
|
|
insert(index, linear_vector<T>{ item });
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove(unsigned index, unsigned count = 1) {
|
|
|
|
for(unsigned i = index; count + i < objectsize; i++) {
|
|
|
|
pool[i] = pool[count + i];
|
|
|
|
}
|
|
|
|
if(count + index >= objectsize) resize(index); //every element >= index was removed
|
|
|
|
else resize(objectsize - count);
|
|
|
|
}
|
|
|
|
|
2011-09-29 12:08:22 +00:00
|
|
|
linear_vector() : pool(nullptr), poolsize(0), objectsize(0) {
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 12:08:22 +00:00
|
|
|
linear_vector(std::initializer_list<T> list) : pool(nullptr), poolsize(0), objectsize(0) {
|
2011-08-08 12:01:09 +00:00
|
|
|
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
|
|
|
}
|
|
|
|
|
|
|
|
~linear_vector() {
|
|
|
|
reset();
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//copy
|
|
|
|
inline linear_vector<T>& operator=(const linear_vector<T> &source) {
|
|
|
|
reset();
|
|
|
|
reserve(source.capacity());
|
|
|
|
resize(source.size());
|
|
|
|
for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2011-09-29 12:08:22 +00:00
|
|
|
linear_vector(const linear_vector<T> &source) : pool(nullptr), poolsize(0), objectsize(0) {
|
2010-08-09 13:28:56 +00:00
|
|
|
operator=(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
//move
|
|
|
|
inline linear_vector<T>& operator=(linear_vector<T> &&source) {
|
|
|
|
reset();
|
|
|
|
pool = source.pool;
|
|
|
|
poolsize = source.poolsize;
|
|
|
|
objectsize = source.objectsize;
|
2011-09-29 12:08:22 +00:00
|
|
|
source.pool = nullptr;
|
2010-08-09 13:28:56 +00:00
|
|
|
source.reset();
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2011-09-29 12:08:22 +00:00
|
|
|
linear_vector(linear_vector<T> &&source) : pool(nullptr), poolsize(0), objectsize(0) {
|
2010-08-09 13:28:56 +00:00
|
|
|
operator=(std::move(source));
|
|
|
|
}
|
|
|
|
|
2011-08-08 12:01:09 +00:00
|
|
|
//index
|
|
|
|
inline T& operator[](unsigned index) {
|
|
|
|
if(index >= objectsize) resize(index + 1);
|
|
|
|
return pool[index];
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2011-08-08 12:01:09 +00:00
|
|
|
inline const T& operator[](unsigned index) const {
|
|
|
|
if(index >= objectsize) throw "vector[] out of bounds";
|
|
|
|
return pool[index];
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2011-08-08 12:01:09 +00:00
|
|
|
//iteration
|
|
|
|
T* begin() { return &pool[0]; }
|
|
|
|
T* end() { return &pool[objectsize]; }
|
|
|
|
const T* begin() const { return &pool[0]; }
|
|
|
|
const T* end() const { return &pool[objectsize]; }
|
2010-08-09 13:28:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//pointer_vector
|
|
|
|
//memory: O(1)
|
|
|
|
//
|
|
|
|
//pointer_vector keeps an array of pointers to each vector object. this adds
|
|
|
|
//significant overhead to individual accesses, but allows for optimal memory
|
|
|
|
//utilization.
|
|
|
|
//
|
|
|
|
//by guaranteeing that the base memory address of each objects never changes,
|
|
|
|
//this avoids the need for an object to have a valid copy constructor.
|
|
|
|
|
2011-10-16 09:44:48 +00:00
|
|
|
template<typename T> struct pointer_vector {
|
2010-08-09 13:28:56 +00:00
|
|
|
protected:
|
|
|
|
T **pool;
|
|
|
|
unsigned poolsize, objectsize;
|
|
|
|
|
|
|
|
public:
|
|
|
|
unsigned size() const { return objectsize; }
|
|
|
|
unsigned capacity() const { return poolsize; }
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
if(pool) {
|
|
|
|
for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; }
|
|
|
|
free(pool);
|
|
|
|
}
|
2011-09-29 12:08:22 +00:00
|
|
|
pool = nullptr;
|
2010-08-09 13:28:56 +00:00
|
|
|
poolsize = 0;
|
|
|
|
objectsize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reserve(unsigned newsize) {
|
|
|
|
newsize = bit::round(newsize); //round to nearest power of two (for amortized growth)
|
|
|
|
|
|
|
|
for(unsigned i = newsize; i < objectsize; i++) {
|
|
|
|
if(pool[i]) { delete pool[i]; pool[i] = 0; }
|
|
|
|
}
|
|
|
|
|
|
|
|
pool = (T**)realloc(pool, newsize * sizeof(T*));
|
|
|
|
for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0;
|
|
|
|
poolsize = newsize;
|
|
|
|
objectsize = min(objectsize, newsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void resize(unsigned newsize) {
|
|
|
|
if(newsize > poolsize) reserve(newsize);
|
|
|
|
|
|
|
|
for(unsigned i = newsize; i < objectsize; i++) {
|
|
|
|
if(pool[i]) { delete pool[i]; pool[i] = 0; }
|
|
|
|
}
|
|
|
|
|
|
|
|
objectsize = newsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void append(const T data) {
|
|
|
|
if(objectsize + 1 > poolsize) reserve(objectsize + 1);
|
|
|
|
pool[objectsize++] = new T(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename U> void insert(unsigned index, const U list) {
|
|
|
|
pointer_vector<T> merged;
|
|
|
|
for(unsigned i = 0; i < index; i++) merged.append(*pool[i]);
|
2011-09-27 11:55:02 +00:00
|
|
|
for(auto &item : list) merged.append(item);
|
2010-08-09 13:28:56 +00:00
|
|
|
for(unsigned i = index; i < objectsize; i++) merged.append(*pool[i]);
|
|
|
|
operator=(merged);
|
|
|
|
}
|
|
|
|
|
|
|
|
void insert(unsigned index, const T item) {
|
|
|
|
insert(index, pointer_vector<T>{ item });
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove(unsigned index, unsigned count = 1) {
|
|
|
|
for(unsigned i = index; count + i < objectsize; i++) {
|
|
|
|
*pool[i] = *pool[count + i];
|
|
|
|
}
|
|
|
|
if(count + index >= objectsize) resize(index); //every element >= index was removed
|
|
|
|
else resize(objectsize - count);
|
|
|
|
}
|
|
|
|
|
2011-09-29 12:08:22 +00:00
|
|
|
pointer_vector() : pool(nullptr), poolsize(0), objectsize(0) {
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 12:08:22 +00:00
|
|
|
pointer_vector(std::initializer_list<T> list) : pool(nullptr), poolsize(0), objectsize(0) {
|
2011-08-08 12:01:09 +00:00
|
|
|
for(const T *p = list.begin(); p != list.end(); ++p) append(*p);
|
|
|
|
}
|
|
|
|
|
|
|
|
~pointer_vector() {
|
|
|
|
reset();
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//copy
|
|
|
|
inline pointer_vector<T>& operator=(const pointer_vector<T> &source) {
|
|
|
|
reset();
|
|
|
|
reserve(source.capacity());
|
|
|
|
resize(source.size());
|
|
|
|
for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2011-09-29 12:08:22 +00:00
|
|
|
pointer_vector(const pointer_vector<T> &source) : pool(nullptr), poolsize(0), objectsize(0) {
|
2010-08-09 13:28:56 +00:00
|
|
|
operator=(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
//move
|
|
|
|
inline pointer_vector<T>& operator=(pointer_vector<T> &&source) {
|
|
|
|
reset();
|
|
|
|
pool = source.pool;
|
|
|
|
poolsize = source.poolsize;
|
|
|
|
objectsize = source.objectsize;
|
2011-09-29 12:08:22 +00:00
|
|
|
source.pool = nullptr;
|
2010-08-09 13:28:56 +00:00
|
|
|
source.reset();
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2011-09-29 12:08:22 +00:00
|
|
|
pointer_vector(pointer_vector<T> &&source) : pool(nullptr), poolsize(0), objectsize(0) {
|
2010-08-09 13:28:56 +00:00
|
|
|
operator=(std::move(source));
|
|
|
|
}
|
|
|
|
|
2011-08-08 12:01:09 +00:00
|
|
|
//index
|
|
|
|
inline T& operator[](unsigned index) {
|
|
|
|
if(index >= objectsize) resize(index + 1);
|
|
|
|
if(!pool[index]) pool[index] = new T;
|
|
|
|
return *pool[index];
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2011-08-08 12:01:09 +00:00
|
|
|
inline const T& operator[](unsigned index) const {
|
|
|
|
if(index >= objectsize || !pool[index]) throw "vector[] out of bounds";
|
|
|
|
return *pool[index];
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
2011-08-08 12:01:09 +00:00
|
|
|
//iteration
|
|
|
|
struct iterator {
|
|
|
|
bool operator!=(const iterator &source) const { return index != source.index; }
|
|
|
|
T& operator*() { return vector.operator[](index); }
|
|
|
|
iterator& operator++() { index++; return *this; }
|
2011-09-27 11:55:02 +00:00
|
|
|
iterator(const pointer_vector &vector, unsigned index) : vector(vector), index(index) {}
|
2011-08-08 12:01:09 +00:00
|
|
|
private:
|
2011-09-27 11:55:02 +00:00
|
|
|
const pointer_vector &vector;
|
2011-08-08 12:01:09 +00:00
|
|
|
unsigned index;
|
|
|
|
};
|
|
|
|
|
|
|
|
iterator begin() { return iterator(*this, 0); }
|
|
|
|
iterator end() { return iterator(*this, objectsize); }
|
2011-09-27 11:55:02 +00:00
|
|
|
const iterator begin() const { return iterator(*this, 0); }
|
|
|
|
const iterator end() const { return iterator(*this, objectsize); }
|
2010-08-09 13:28:56 +00:00
|
|
|
};
|
2012-01-26 06:50:09 +00:00
|
|
|
#endif
|
2010-08-09 13:28:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|