Add cmake files and initial memcpy/string implementation
This commit is contained in:
parent
d397a376a5
commit
29aa5bdc5b
17
CMakeLists.txt
Normal file
17
CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
project(mtl LANGUAGES CXX C ASM)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
set(TARGET_SRC_FILES src/memcpy.s src/string.cpp)
|
||||||
|
set(TARGET_PUB_INCLUDE_FILES include/utility.hpp include/string.hpp)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} STATIC ${TARGET_SRC_FILES} ${TARGET_PUB_INCLUDE_FILES})
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC "include")
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${TARGET_PUB_INCLUDE_FILES}")
|
||||||
|
|
||||||
|
install(TARGETS ${PROJECT_NAME}
|
||||||
|
LIBRARY DESTINATION lib
|
||||||
|
PUBLIC_HEADER DESTINATION include)
|
||||||
243
include/mtl/string.hpp
Normal file
243
include/mtl/string.hpp
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Generic string interface
|
||||||
|
*
|
||||||
|
* Interface for mtl::string and mtl::string_ext.
|
||||||
|
* Implements most functions from std::string, see std::string documentation
|
||||||
|
* for function descriptions. Some operations are unimplemented due to the
|
||||||
|
* lack of dynamic memory usage.
|
||||||
|
*/
|
||||||
|
class istring {
|
||||||
|
protected:
|
||||||
|
char* m_str;
|
||||||
|
size_t m_size;
|
||||||
|
size_t m_capacity;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef char* iterator;
|
||||||
|
typedef const char* const_iterator;
|
||||||
|
typedef std::reverse_iterator<iterator> reverse_iterator;
|
||||||
|
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||||
|
|
||||||
|
static constexpr const size_t npos = -1;
|
||||||
|
|
||||||
|
istring(char* _str, size_t _capacity, size_t _size);
|
||||||
|
|
||||||
|
// Data functions
|
||||||
|
|
||||||
|
// If the new string does not fit, it will be truncated to the capacity
|
||||||
|
istring& assign(const istring& str);
|
||||||
|
istring& assign(const istring& str, size_t pos, size_t count = npos);
|
||||||
|
istring& assign(const char* str, size_t count);
|
||||||
|
istring& assign(const char* str);
|
||||||
|
istring& assign(size_t count, char ch);
|
||||||
|
template <typename T>
|
||||||
|
istring& assign(T first, T last) {
|
||||||
|
m_size = 0;
|
||||||
|
T it = first;
|
||||||
|
while (it != last && m_size < m_capacity) {
|
||||||
|
m_str[m_size] = *it;
|
||||||
|
++m_size;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
istring& operator=(const istring& rhs);
|
||||||
|
istring& operator=(const char* rhs);
|
||||||
|
istring& operator=(char rhs);
|
||||||
|
|
||||||
|
// Element access functions
|
||||||
|
|
||||||
|
char& at(size_t i);
|
||||||
|
const char& at(size_t i) const;
|
||||||
|
|
||||||
|
char& operator[](size_t i);
|
||||||
|
const char& operator[](size_t i) const;
|
||||||
|
|
||||||
|
char& front();
|
||||||
|
const char& front() const;
|
||||||
|
|
||||||
|
char& back();
|
||||||
|
const char& back() const;
|
||||||
|
|
||||||
|
char* data();
|
||||||
|
const char* data() const;
|
||||||
|
const char* c_str() const;
|
||||||
|
|
||||||
|
// Iterator functions
|
||||||
|
|
||||||
|
iterator begin();
|
||||||
|
const_iterator begin() const;
|
||||||
|
const_iterator cbegin() const;
|
||||||
|
|
||||||
|
iterator end();
|
||||||
|
const_iterator end() const;
|
||||||
|
const_iterator cend() const;
|
||||||
|
|
||||||
|
reverse_iterator rbegin();
|
||||||
|
const_reverse_iterator rbegin() const;
|
||||||
|
const_reverse_iterator crbegin() const;
|
||||||
|
|
||||||
|
reverse_iterator rend();
|
||||||
|
const_reverse_iterator rend() const;
|
||||||
|
const_reverse_iterator crend() const;
|
||||||
|
|
||||||
|
// Capacity
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
size_t size() const;
|
||||||
|
size_t length() const;
|
||||||
|
|
||||||
|
size_t capacity() const;
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
// Insert from self only works if index is after input range
|
||||||
|
istring& insert(size_t index, const istring& str);
|
||||||
|
istring& insert(size_t index, const istring& str, size_t s_index, size_t count = npos);
|
||||||
|
istring& insert(size_t index, const char* str);
|
||||||
|
istring& insert(size_t index, const char* str, size_t count);
|
||||||
|
istring& insert(size_t index, size_t count, char ch);
|
||||||
|
iterator insert(const_iterator pos, char ch);
|
||||||
|
iterator insert(const_iterator pos, size_t count, char ch);
|
||||||
|
template <typename T>
|
||||||
|
iterator insert(const_iterator pos, T first, T last) {
|
||||||
|
}
|
||||||
|
|
||||||
|
istring& erase(size_t index = 0, size_t count = npos);
|
||||||
|
iterator erase(const_iterator pos);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
|
||||||
|
void push_back(char ch);
|
||||||
|
void pop_back();
|
||||||
|
|
||||||
|
istring& append(const istring& str);
|
||||||
|
istring& append(const istring& str, size_t s_index, size_t count = npos);
|
||||||
|
istring& append(const char* str);
|
||||||
|
istring& append(const char* str, size_t count);
|
||||||
|
istring& append(size_t count, char ch);
|
||||||
|
template <typename T>
|
||||||
|
istring& append(T first, T last) {
|
||||||
|
}
|
||||||
|
|
||||||
|
istring& operator+=(char ch);
|
||||||
|
istring& operator+=(const char* str);
|
||||||
|
istring& operator+=(const istring& str);
|
||||||
|
|
||||||
|
istring& replace(size_t pos, size_t count, const istring& str);
|
||||||
|
istring& replace(const_iterator first, const_iterator last, const istring& str);
|
||||||
|
istring& replace(size_t pos, size_t count, const istring& str, size_t pos2, size_t count2 = npos);
|
||||||
|
istring& replace(size_t pos, size_t count, const char* str, size_t count2);
|
||||||
|
istring& replace(const_iterator first, const_iterator last, const char* str, size_t count2);
|
||||||
|
istring& replace(size_t pos, size_t count, const char* str);
|
||||||
|
istring& replace(const_iterator first, const_iterator last, const char* str);
|
||||||
|
istring& replace(size_t pos, size_t count, size_t count2, char ch);
|
||||||
|
istring& replace(const_iterator first, const_iterator last, size_t count2, char ch);
|
||||||
|
template <typename T>
|
||||||
|
istring& replace(const_iterator first, const_iterator last, T first2, T last2);
|
||||||
|
|
||||||
|
size_t copy(char* dest, size_t count, size_t pos = 0) const;
|
||||||
|
|
||||||
|
void resize(size_t count);
|
||||||
|
void resize(size_t count, char ch);
|
||||||
|
|
||||||
|
// Search
|
||||||
|
|
||||||
|
size_t find(const istring& str, size_t pos = 0) const;
|
||||||
|
size_t find(const char* str, size_t pos, size_t count) const;
|
||||||
|
size_t find(const char* str, size_t pos = 0) const;
|
||||||
|
size_t find(char ch, size_t pos = 0) const;
|
||||||
|
|
||||||
|
size_t rfind(const istring& str, size_t pos = npos) const;
|
||||||
|
size_t rfind(const char* str, size_t pos, size_t count) const;
|
||||||
|
size_t rfind(const char* str, size_t pos = npos) const;
|
||||||
|
size_t rfind(char ch, size_t pos = npos) const;
|
||||||
|
|
||||||
|
size_t find_first_of(const istring& str, size_t pos = 0) const;
|
||||||
|
size_t find_first_of(const char* str, size_t pos, size_t count) const;
|
||||||
|
size_t find_first_of(const char* str, size_t pos = 0) const;
|
||||||
|
size_t find_first_of(char ch, size_t pos = 0) const;
|
||||||
|
|
||||||
|
size_t find_first_not_of(const istring& str, size_t pos = 0) const;
|
||||||
|
size_t find_first_not_of(const char* str, size_t pos, size_t count) const;
|
||||||
|
size_t find_first_not_of(const char* str, size_t pos = 0) const;
|
||||||
|
size_t find_first_not_of(char ch, size_t pos = 0) const;
|
||||||
|
|
||||||
|
size_t find_last_of(const istring& str, size_t pos = npos) const;
|
||||||
|
size_t find_last_of(const char* str, size_t pos, size_t count) const;
|
||||||
|
size_t find_last_of(const char* str, size_t pos = npos) const;
|
||||||
|
size_t find_last_of(char ch, size_t pos = npos) const;
|
||||||
|
|
||||||
|
size_t find_last_not_of(const istring& str, size_t pos = npos) const;
|
||||||
|
size_t find_last_not_of(const char* str, size_t pos, size_t count) const;
|
||||||
|
size_t find_last_not_of(const char* str, size_t pos = npos) const;
|
||||||
|
size_t find_last_not_of(char ch, size_t pos = npos) const;
|
||||||
|
|
||||||
|
// Comparison
|
||||||
|
|
||||||
|
int32_t compare(const istring& str) const;
|
||||||
|
int32_t compare(size_t pos, size_t count, const istring& str) const;
|
||||||
|
int32_t compare(size_t pos1, size_t count1, istring& str, size_t pos2, size_t count2 = npos) const;
|
||||||
|
int32_t compare(const char* str) const;
|
||||||
|
int32_t compare(size_t pos, size_t count, const char* str) const;
|
||||||
|
int32_t compare(size_t pos1, size_t count1, const char* str, size_t count2) const;
|
||||||
|
|
||||||
|
friend bool operator==(const istring& lhs, const istring& rhs);
|
||||||
|
friend bool operator!=(const istring& lhs, const istring& rhs);
|
||||||
|
friend bool operator<(const istring& lhs, const istring& rhs);
|
||||||
|
friend bool operator<=(const istring& lhs, const istring& rhs);
|
||||||
|
friend bool operator>(const istring& lhs, const istring& rhs);
|
||||||
|
friend bool operator>=(const istring& lhs, const istring& rhs);
|
||||||
|
|
||||||
|
friend bool operator==(const istring& lhs, const char* rhs);
|
||||||
|
friend bool operator==(const char* lhs, const istring& rhs);
|
||||||
|
friend bool operator!=(const istring& lhs, const char* rhs);
|
||||||
|
friend bool operator!=(const char* lhs, const istring& rhs);
|
||||||
|
friend bool operator<(const istring& lhs, const char* rhs);
|
||||||
|
friend bool operator<(const char* lhs, const istring& rhs);
|
||||||
|
friend bool operator<=(const istring& lhs, const char* rhs);
|
||||||
|
friend bool operator<=(const char* lhs, const istring& rhs);
|
||||||
|
friend bool operator>(const istring& lhs, const char* rhs);
|
||||||
|
friend bool operator>(const char* lhs, const istring& rhs);
|
||||||
|
friend bool operator>=(const istring& lhs, const char* rhs);
|
||||||
|
friend bool operator>=(const char* lhs, const istring& rhs);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t C>
|
||||||
|
class string : public istring {
|
||||||
|
private:
|
||||||
|
char m_buf[C + 1];
|
||||||
|
|
||||||
|
public:
|
||||||
|
string() : istring(m_buf, C, 0) {}
|
||||||
|
string(const char* str) : istring(m_buf, C, 0) {
|
||||||
|
m_size = std::strlen(str);
|
||||||
|
if (m_size > C) { m_size = m_capacity; }
|
||||||
|
|
||||||
|
mtl::memcpy(m_buf, str, m_size);
|
||||||
|
m_str[m_size] = '\0';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class string_ext : public istring {
|
||||||
|
public:
|
||||||
|
string_ext(char* buf, size_t buf_size);
|
||||||
|
string_ext(const char&) = delete;
|
||||||
|
|
||||||
|
using istring::operator=;
|
||||||
|
};
|
||||||
|
} // namespace ml
|
||||||
21
include/mtl/utility.hpp
Normal file
21
include/mtl/utility.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void mtl_memcpy32(void* dst, const void* src, size_t num8);
|
||||||
|
void mtl_dumbcpy16(void* dst, const void* src, size_t num8);
|
||||||
|
void mtl_dumbcpy(void* dst, const void* src, size_t num8);
|
||||||
|
void mtl_hybridcpy(void* dst, const void* src, size_t num8);
|
||||||
|
void mtl_rmemcpy32(void* dst, const void* src, size_t num8);
|
||||||
|
void mtl_rdumbcpy16(void* dst, const void* src, size_t num8);
|
||||||
|
void mtl_rdumbcpy(void* dst, const void* src, size_t num8);
|
||||||
|
void mtl_rhybridcpy(void* dst, const void* src, size_t num8);
|
||||||
|
void mtl_hybridmove(void* dst, const void* src, size_t num8);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
constexpr auto memcpy = mtl_hybridcpy;
|
||||||
|
constexpr auto memmove = mtl_hybridmove;
|
||||||
|
}
|
||||||
213
src/memcpy.s
Normal file
213
src/memcpy.s
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
// Load as ARM instructions into GBA IWRAM section.
|
||||||
|
// IWRAM section has a 32bit buffer instead of 16bit, so it is
|
||||||
|
// able to load arm instructions faster.
|
||||||
|
.section .iwram, "ax", %progbits
|
||||||
|
.arm
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
// Copies num_byte from dst to src.
|
||||||
|
// dst and src !! MUST BE WORD ALIGNED !!
|
||||||
|
// however, num_byte does not need to be aligned
|
||||||
|
// r0, r1: dst, src
|
||||||
|
// r2: num_byte
|
||||||
|
.global mtl_memcpy32
|
||||||
|
.type mtl_memcpy32 STT_FUNC
|
||||||
|
mtl_memcpy32:
|
||||||
|
// r12 = num_residual_byte
|
||||||
|
and r12, r2, #31
|
||||||
|
// r2 = num_chunk
|
||||||
|
lsrs r2, r2, #5
|
||||||
|
// Skip chunk copy if there are no chunks
|
||||||
|
beq .Lword_process
|
||||||
|
.Lchunk_process:
|
||||||
|
// Preserve local variables of calling function
|
||||||
|
push {r4-r10}
|
||||||
|
.Lchunk_copy:
|
||||||
|
// Load 8 word chunk from src into registers, increment src after
|
||||||
|
ldmia r1!, {r3-r10}
|
||||||
|
// Store 8 word chunk from register into dst, increment dst after
|
||||||
|
stmia r0!, {r3-r10}
|
||||||
|
// Copy again if more than zero chunks left
|
||||||
|
subs r2, r2, #1
|
||||||
|
bhi .Lchunk_copy
|
||||||
|
// Restore local variables of calling function
|
||||||
|
pop {r4-r10}
|
||||||
|
.Lword_process:
|
||||||
|
// r2 = num_word
|
||||||
|
lsrs r2, r12, #2
|
||||||
|
// Skip word copy if there are no words
|
||||||
|
beq .Lbyte_process
|
||||||
|
.Lword_copy:
|
||||||
|
// Load word from src into register, increment src after
|
||||||
|
ldr r3, [r1], #4
|
||||||
|
// Store word from register into dst, increment dst after
|
||||||
|
str r3, [r0], #4
|
||||||
|
// Copy again if more than zero words left
|
||||||
|
subs r2, r2, #1
|
||||||
|
bhi .Lword_copy
|
||||||
|
.Lbyte_process:
|
||||||
|
// r12 = num_residual_byte
|
||||||
|
and r12, r12, #3
|
||||||
|
.Lbyte_copy:
|
||||||
|
// Decrement byte count ahead of time, by checking the carry bit
|
||||||
|
// during load/store/branch, we can avoid an unnecessary branch.
|
||||||
|
// !REMEMBER, carry = !borrow. Carry is set when subtraction does
|
||||||
|
// not underflow. IE. num of bytes was > 0
|
||||||
|
subs r12, r12, #1
|
||||||
|
// Load byte from src into register, increment src after
|
||||||
|
ldrcsb r3, [r1], #1
|
||||||
|
// Load byte from register into dst, increment dst after
|
||||||
|
strcsb r3, [r0], #1
|
||||||
|
// Copy again if more bytes left, checks for zero and carry bit set
|
||||||
|
bhi .Lbyte_copy
|
||||||
|
// Return
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.global mtl_dumbcpy16
|
||||||
|
.type mtl_dumbcpy16 STT_FUNC
|
||||||
|
mtl_dumbcpy16:
|
||||||
|
// r12 = has residual byte
|
||||||
|
and r12, r2, #1
|
||||||
|
// r2 = num_hword
|
||||||
|
lsr r2, r2, #1
|
||||||
|
.Lhword_copy:
|
||||||
|
// Copy half words
|
||||||
|
subs r2, r2, #1
|
||||||
|
ldrcsh r3, [r1], #2
|
||||||
|
strcsh r3, [r0], #2
|
||||||
|
bhi .Lhword_copy
|
||||||
|
// Copy residual byte if needed
|
||||||
|
cmp r12, #0
|
||||||
|
ldrneb r3, [r1]
|
||||||
|
strneb r3, [r0]
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
// Performs a generic byte-by-byte memcpy.
|
||||||
|
// Still faster than std::memcpy due to being put in IWRAM
|
||||||
|
// r0, r1: dst, src
|
||||||
|
// r2: num_bytes
|
||||||
|
.global mtl_dumbcpy
|
||||||
|
.type mtl_dumbcpy STT_FUNC
|
||||||
|
mtl_dumbcpy:
|
||||||
|
subs r2, r2, #1
|
||||||
|
ldrcsb r3, [r1], #1
|
||||||
|
strcsb r3, [r0], #1
|
||||||
|
bhi mtl_dumbcpy
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
// Calls mtl_memcpy32 if src and dst are word aligned,
|
||||||
|
// otherwise calls mtl_dumbcpy
|
||||||
|
.global mtl_hybridcpy
|
||||||
|
.type mtl_hybridcpy STT_FUNC
|
||||||
|
mtl_hybridcpy:
|
||||||
|
orr r3, r0, r1
|
||||||
|
ands r12, r3, #1
|
||||||
|
bne mtl_dumbcpy
|
||||||
|
ands r12, r3, #2
|
||||||
|
bne mtl_dumbcpy16
|
||||||
|
b mtl_memcpy32
|
||||||
|
|
||||||
|
// asdf
|
||||||
|
.global mtl_rmemcpy32
|
||||||
|
.type mtl_rmemcpy32 STT_FUNC
|
||||||
|
mtl_rmemcpy32:
|
||||||
|
// Move to last byte of src and dst
|
||||||
|
add r0, r2
|
||||||
|
add r1, r2
|
||||||
|
sub r0, #1
|
||||||
|
sub r1, #1
|
||||||
|
// r12 = num residual bytes
|
||||||
|
and r12, r2, #3
|
||||||
|
.Lrbyte_copy:
|
||||||
|
subs r12, #1
|
||||||
|
ldrcsb r3, [r1], #-1
|
||||||
|
strcsb r3, [r0], #-1
|
||||||
|
bhi .Lrbyte_copy
|
||||||
|
// r12 = num residual words
|
||||||
|
lsr r12, r2, #2
|
||||||
|
// Move to the beginning of the current word
|
||||||
|
sub r0, #3
|
||||||
|
sub r1, #3
|
||||||
|
.Lrword_copy:
|
||||||
|
subs r12, #1
|
||||||
|
ldrcs r3, [r1], #-4
|
||||||
|
strcs r3, [r0], #-4
|
||||||
|
bhi .Lrword_copy
|
||||||
|
// r2 = num chunks
|
||||||
|
lsr r2, #5
|
||||||
|
// Preserve local variables
|
||||||
|
push {r4-r10}
|
||||||
|
.Lrchunk_copy:
|
||||||
|
subs r2, #1
|
||||||
|
ldmcsda r1!, {r3-r10}
|
||||||
|
stmcsda r0!, {r3-r10}
|
||||||
|
bhi .Lrchunk_copy
|
||||||
|
// Restore local variables
|
||||||
|
pop {r4-r10}
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
// Performs a copy a halfword at a time, in reverse.
|
||||||
|
.global mtl_rdumbcpy16
|
||||||
|
.type mtl_rdumbcpy16 STT_FUNC
|
||||||
|
mtl_rdumbcpy16:
|
||||||
|
// Move to last byte of src and dst
|
||||||
|
add r0, r2
|
||||||
|
add r1, r2
|
||||||
|
sub r0, #1
|
||||||
|
sub r1, #1
|
||||||
|
// r12 = has residual byte
|
||||||
|
and r12, r2, #1
|
||||||
|
// Copy residual byte if there is one
|
||||||
|
ldrneb r3, [r1], #-1
|
||||||
|
strneb r3, [r0], #-1
|
||||||
|
// r2 = num of half words
|
||||||
|
lsrs r2, #1
|
||||||
|
.Lrhword_copy:
|
||||||
|
subs r2, #1
|
||||||
|
ldrcsh r3, [r1], #-2
|
||||||
|
strcsh r3, [r0], #-2
|
||||||
|
bhi .Lrhword_copy
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
|
||||||
|
// Performs a generic byte-by-byte memcpy in reverse.
|
||||||
|
// This allows a safe copy when the dst and src overlap,
|
||||||
|
// and the destination is after the source
|
||||||
|
.global mtl_rdumbcpy
|
||||||
|
.type mtl_rdumbcpy STT_FUNC
|
||||||
|
mtl_rdumbcpy:
|
||||||
|
add r0, r2
|
||||||
|
add r1, r2
|
||||||
|
sub r0, #1
|
||||||
|
sub r1, #1
|
||||||
|
.Lcpy:
|
||||||
|
subs r2, #1
|
||||||
|
ldrcsb r3, [r1], #-1
|
||||||
|
strcsb r3, [r0], #-1
|
||||||
|
bhi .Lcpy
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
// Performs a reverse copy, choosing a function depending on the alignment
|
||||||
|
.global mtl_rhybridcpy
|
||||||
|
.type mtl_rhybridcpy STT_FUNC
|
||||||
|
mtl_rhybridcpy:
|
||||||
|
orr r3, r0, r1
|
||||||
|
// Dumb copy if only byte aligned, discard result
|
||||||
|
ands r12, r3, #1
|
||||||
|
bne mtl_rdumbcpy
|
||||||
|
// Dumb copy by halfword if only halfword aligned
|
||||||
|
ands r12, r3, #2
|
||||||
|
bne mtl_rdumbcpy16
|
||||||
|
// Otherwise it's safe to copy by word
|
||||||
|
b mtl_rmemcpy32
|
||||||
|
|
||||||
|
.global mtl_hybridmove
|
||||||
|
.type mtl_hybridmove STT_FUNC
|
||||||
|
mtl_hybridmove:
|
||||||
|
cmp r0, r1
|
||||||
|
bhi mtl_rhybridcpy
|
||||||
|
blo mtl_hybridcpy
|
||||||
|
// src and dst are the same, no need to copy
|
||||||
|
bx lr
|
||||||
|
|
||||||
217
src/string.cpp
Normal file
217
src/string.cpp
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#include "mtl/string.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "mtl/utility.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
istring::istring(char* _str, size_t _capacity, size_t _size)
|
||||||
|
: m_str(_str), m_capacity(_capacity), m_size(_size) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ===== assign =====
|
||||||
|
*/
|
||||||
|
|
||||||
|
istring& istring::assign(const istring& str) {
|
||||||
|
if (&str == this) { return *this; }
|
||||||
|
|
||||||
|
m_size = str.m_size;
|
||||||
|
if (m_size > m_capacity) { m_size = m_capacity; }
|
||||||
|
|
||||||
|
std::memcpy(m_str, str.m_str, m_size);
|
||||||
|
m_str[m_size] = '\0';
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
istring& istring::assign(const istring& str, size_t pos, size_t count) {
|
||||||
|
if (pos + count > str.m_size) { m_size = str.m_size - pos; }
|
||||||
|
else { m_size = count; }
|
||||||
|
|
||||||
|
if (m_size > m_capacity) { m_size = m_capacity; }
|
||||||
|
|
||||||
|
// str may overlap
|
||||||
|
std::memmove(m_str, str.m_str + pos, m_size);
|
||||||
|
m_str[m_size] = '\0';
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
istring& istring::assign(const char* str, size_t count) {
|
||||||
|
m_size = count;
|
||||||
|
if (m_size > m_capacity) { m_size = m_capacity; }
|
||||||
|
|
||||||
|
// str may overlap
|
||||||
|
std::memmove(m_str, str, m_size);
|
||||||
|
m_str[m_size] = '\0';
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
istring& istring::assign(const char* str) {
|
||||||
|
m_size = std::strlen(str);
|
||||||
|
if (m_size > m_capacity) { m_size = m_capacity; }
|
||||||
|
|
||||||
|
// str may overlap
|
||||||
|
mtl::memcpy(m_str, str, m_size);
|
||||||
|
m_str[m_size] = '\0';
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
istring& istring::assign(size_t count, char ch) {
|
||||||
|
m_size = count;
|
||||||
|
if (m_size > m_capacity) { m_size = m_capacity; }
|
||||||
|
|
||||||
|
std::memset(m_str, ch, m_size);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ===== operator= =====
|
||||||
|
*/
|
||||||
|
|
||||||
|
istring& istring::operator=(const istring& rhs) {
|
||||||
|
return assign(rhs);
|
||||||
|
}
|
||||||
|
istring& istring::operator=(const char* rhs) {
|
||||||
|
return assign(rhs);
|
||||||
|
}
|
||||||
|
istring& istring::operator=(char rhs) {
|
||||||
|
return assign(1, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ===== access =====
|
||||||
|
*/
|
||||||
|
|
||||||
|
char& istring::at(size_t i) {
|
||||||
|
return m_str[i];
|
||||||
|
}
|
||||||
|
const char& istring::at(size_t i) const {
|
||||||
|
return m_str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
char& istring::operator[](size_t i) {
|
||||||
|
return m_str[i];
|
||||||
|
}
|
||||||
|
const char& istring::operator[](size_t i) const {
|
||||||
|
return m_str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
char& istring::front() {
|
||||||
|
return *m_str;
|
||||||
|
}
|
||||||
|
const char& istring::front() const {
|
||||||
|
return *m_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
char& istring::back() {
|
||||||
|
return m_str[m_size];
|
||||||
|
}
|
||||||
|
const char& istring::back() const {
|
||||||
|
return m_str[m_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
char* istring::data() {
|
||||||
|
return m_str;
|
||||||
|
}
|
||||||
|
const char* istring::data() const {
|
||||||
|
return m_str;
|
||||||
|
}
|
||||||
|
const char* istring::c_str() const {
|
||||||
|
return m_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ===== iterators =====
|
||||||
|
*/
|
||||||
|
|
||||||
|
istring::iterator istring::begin() {
|
||||||
|
return m_str;
|
||||||
|
}
|
||||||
|
istring::const_iterator istring::begin() const {
|
||||||
|
return m_str;
|
||||||
|
}
|
||||||
|
istring::const_iterator istring::cbegin() const {
|
||||||
|
return m_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
istring::iterator istring::end() {
|
||||||
|
return m_str + m_size;
|
||||||
|
}
|
||||||
|
istring::const_iterator istring::end() const {
|
||||||
|
return m_str + m_size;
|
||||||
|
}
|
||||||
|
istring::const_iterator istring::cend() const {
|
||||||
|
return m_str + m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
istring::reverse_iterator istring::rbegin() {
|
||||||
|
return reverse_iterator(m_str + m_size);
|
||||||
|
}
|
||||||
|
istring::const_reverse_iterator istring::rbegin() const {
|
||||||
|
return reverse_iterator(m_str + m_size);
|
||||||
|
}
|
||||||
|
istring::const_reverse_iterator istring::crbegin() const {
|
||||||
|
return reverse_iterator(m_str + m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
istring::reverse_iterator istring::rend() {
|
||||||
|
return reverse_iterator(m_str);
|
||||||
|
}
|
||||||
|
istring::const_reverse_iterator istring::rend() const {
|
||||||
|
return reverse_iterator(m_str);
|
||||||
|
}
|
||||||
|
istring::const_reverse_iterator istring::crend() const {
|
||||||
|
return reverse_iterator(m_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ===== capacity =====
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool istring::empty() const {
|
||||||
|
return m_size > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t istring::size() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
size_t istring::length() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t istring::capacity() const {
|
||||||
|
return m_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void istring::clear() {
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
istring& istring::insert(size_t index, const istring& str) {
|
||||||
|
char* substr = m_str + index;
|
||||||
|
|
||||||
|
size_t space = m_capacity - index;
|
||||||
|
if (space > str.m_size) { // there is room to move
|
||||||
|
size_t space_to_move = space - str.m_size;
|
||||||
|
size_t items_to_move = m_size - index;
|
||||||
|
// only move items if there is room, and they exist
|
||||||
|
size_t num_to_move = std::min(items_to_move, space_to_move);
|
||||||
|
mtl::memmove(substr + str.m_size, substr, num_to_move);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only copy the string if there is room
|
||||||
|
size_t num_to_copy = std::min(str.m_size, space);
|
||||||
|
mtl::memcpy(substr, str.m_str, num_to_copy);
|
||||||
|
|
||||||
|
m_size += num_to_copy;
|
||||||
|
m_str[m_size] = '\0';
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string_ext::string_ext(char* buf, size_t buf_size) : istring(buf, buf_size - 1, 0) {}
|
||||||
|
|
||||||
|
|
||||||
|
} //namespace ml
|
||||||
Loading…
x
Reference in New Issue
Block a user