mirror of
https://github.com/jakcron/nstool
synced 2024-11-22 21:49:30 +00:00
[fnd] Added Vec, changed List. Remove MemoryBlob, replaced with Vec<byte_t>.
This commit is contained in:
parent
43d243824c
commit
9b3a26806a
8 changed files with 404 additions and 278 deletions
|
@ -126,9 +126,9 @@
|
|||
<ClInclude Include="include\fnd\Exception.h" />
|
||||
<ClInclude Include="include\fnd\IFile.h" />
|
||||
<ClInclude Include="include\fnd\io.h" />
|
||||
<ClInclude Include="include\fnd\ISerialiseableBinary.h" />
|
||||
<ClInclude Include="include\fnd\ISerialisable.h" />
|
||||
<ClInclude Include="include\fnd\List.h" />
|
||||
<ClInclude Include="include\fnd\MemoryBlob.h" />
|
||||
<ClInclude Include="include\fnd\Vec.h" />
|
||||
<ClInclude Include="include\fnd\ResourceFileReader.h" />
|
||||
<ClInclude Include="include\fnd\SimpleFile.h" />
|
||||
<ClInclude Include="include\fnd\SimpleTextOutput.h" />
|
||||
|
@ -138,7 +138,6 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="source\Exception.cpp" />
|
||||
<ClCompile Include="source\io.cpp" />
|
||||
<ClCompile Include="source\MemoryBlob.cpp" />
|
||||
<ClCompile Include="source\ResourceFileReader.cpp" />
|
||||
<ClCompile Include="source\SimpleFile.cpp" />
|
||||
<ClCompile Include="source\SimpleTextOutput.cpp" />
|
||||
|
|
|
@ -24,15 +24,6 @@
|
|||
<ClInclude Include="include\fnd\io.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\fnd\ISerialiseableBinary.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\fnd\List.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\fnd\MemoryBlob.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\fnd\StringConv.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -57,6 +48,15 @@
|
|||
<ClInclude Include="include\fnd\SimpleFile.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\fnd\List.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\fnd\Vec.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\fnd\ISerialisable.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="source\Exception.cpp">
|
||||
|
@ -65,9 +65,6 @@
|
|||
<ClCompile Include="source\io.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="source\MemoryBlob.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="source\StringConv.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
21
lib/libfnd/include/fnd/ISerialisable.h
Normal file
21
lib/libfnd/include/fnd/ISerialisable.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include <fnd/types.h>
|
||||
#include <fnd/Vec.h>
|
||||
|
||||
namespace fnd
|
||||
{
|
||||
class ISerialisable
|
||||
{
|
||||
public:
|
||||
// serialise
|
||||
virtual void toBytes() = 0;
|
||||
// deserialise
|
||||
virtual void fromBytes(const byte_t* data, size_t len) = 0;
|
||||
|
||||
// get byte vector
|
||||
virtual const fnd::Vec<byte_t>& getBytes() const = 0;
|
||||
|
||||
// clear data
|
||||
virtual void clear() = 0;
|
||||
};
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
#pragma once
|
||||
#include <fnd/types.h>
|
||||
|
||||
namespace fnd
|
||||
{
|
||||
class ISerialiseableBinary
|
||||
{
|
||||
public:
|
||||
virtual const byte_t* getBytes() const = 0;
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
virtual void exportBinary() = 0;
|
||||
virtual void importBinary(const byte_t* bytes, size_t len) = 0;
|
||||
|
||||
virtual void clear() = 0;
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
#include <fnd/types.h>
|
||||
#include <vector>
|
||||
#include <fnd/Vec.h>
|
||||
|
||||
namespace fnd
|
||||
{
|
||||
|
@ -8,130 +8,199 @@ namespace fnd
|
|||
class List
|
||||
{
|
||||
public:
|
||||
List() :
|
||||
mElements()
|
||||
{
|
||||
}
|
||||
// constructors
|
||||
List();
|
||||
List(const List<T>& other);
|
||||
|
||||
List(const T* elements, size_t num) :
|
||||
mElements(num)
|
||||
{
|
||||
initList(elements, num);
|
||||
}
|
||||
// copy operator
|
||||
const List<T>& operator=(const List<T>& other);
|
||||
|
||||
// assignment operator
|
||||
const List& operator=(const List& other)
|
||||
{
|
||||
mElements.clear();
|
||||
for (size_t i = 0; i < other.getSize(); i++)
|
||||
{
|
||||
mElements.push_back(other[i]);
|
||||
}
|
||||
// equivalence operators
|
||||
bool operator==(const List<T>& other) const;
|
||||
bool operator!=(const List<T>& other) const;
|
||||
|
||||
return *this;
|
||||
}
|
||||
// back relative insertion
|
||||
void addElement(const T& element);
|
||||
|
||||
// element access
|
||||
const T& operator[](size_t index) const;
|
||||
T& operator[](size_t index);
|
||||
const T& atBack() const;
|
||||
T& atBack();
|
||||
|
||||
// comparision operator
|
||||
bool operator==(const List& other) const
|
||||
{
|
||||
if (other.getSize() != this->getSize())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// element num
|
||||
size_t size() const;
|
||||
|
||||
for (size_t i = 0; i < this->getSize(); i++)
|
||||
{
|
||||
if (getElement(i) != other[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// clear List
|
||||
void clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const List& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
// access operators
|
||||
const T& getElement(size_t index) const
|
||||
{
|
||||
return mElements[index];
|
||||
}
|
||||
T& getElement(size_t index)
|
||||
{
|
||||
if (index == mElements.size()) mElements.push_back(T());
|
||||
return mElements[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const { return getElement(index); }
|
||||
T& operator[](size_t index) { return getElement(index); }
|
||||
const T& atBack() const { return getElement(getSize() - 1); }
|
||||
T& atBack() { return getElement(getSize() - 1); }
|
||||
|
||||
// functions
|
||||
void addElement(const T& element) { mElements.push_back(element); }
|
||||
size_t getIndexOf(const T& key) const
|
||||
{
|
||||
for (size_t i = 0; i < getSize(); i++)
|
||||
{
|
||||
if (getElement(i) == key) return i;
|
||||
}
|
||||
|
||||
throw Exception("LIST", "Element does not exist");
|
||||
}
|
||||
bool hasElement(const T& key) const
|
||||
{
|
||||
try
|
||||
{
|
||||
getIndexOf(key);
|
||||
} catch (const Exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// special
|
||||
template <class X>
|
||||
size_t getIndexOf(const X& key) const
|
||||
{
|
||||
for (size_t i = 0; i < getSize(); i++)
|
||||
{
|
||||
if (getElement(i) == key) return i;
|
||||
}
|
||||
|
||||
throw Exception("LIST", "Element does not exist");
|
||||
}
|
||||
template <class X>
|
||||
bool hasElement(const X& key) const
|
||||
{
|
||||
try
|
||||
{
|
||||
getIndexOf(key);
|
||||
} catch (const Exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
size_t getSize() const { return mElements.size(); }
|
||||
void clear() { mElements.clear(); }
|
||||
// element access by key
|
||||
template <class K>
|
||||
bool hasElement(const K& key) const;
|
||||
template <class K>
|
||||
const T& getElement(const K& key) const;
|
||||
template <class K>
|
||||
T& getElement(const K& key);
|
||||
|
||||
private:
|
||||
std::vector<T> mElements;
|
||||
static const size_t kDefaultSize = 20;
|
||||
|
||||
void initList(T* elements, size_t num)
|
||||
fnd::Vec<T> m_Vec;
|
||||
size_t m_Num;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline List<T>::List() :
|
||||
m_Vec(),
|
||||
m_Num(0)
|
||||
{
|
||||
m_Vec.alloc(kDefaultSize);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline List<T>::List(const List<T>& other) :
|
||||
m_Vec(other.m_Vec),
|
||||
m_Size(other.m_Size)
|
||||
{}
|
||||
|
||||
template<class T>
|
||||
inline const List<T>& List<T>::operator=(const List<T>& other)
|
||||
{
|
||||
m_Vec = other.m_Vec;
|
||||
m_Size = other.m_Size;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline bool List<T>::operator==(const List<T>& other) const
|
||||
{
|
||||
bool isEqual = true;
|
||||
|
||||
if (m_Num == other.m_Num)
|
||||
{
|
||||
mElements.clear();
|
||||
for (size_t i = 0; i < num; i++)
|
||||
for (size_t i = 0; i < m_Num && isEqual == true; i++)
|
||||
{
|
||||
mElements.push_back(elements[i]);
|
||||
if ((*this)[i] != other[i])
|
||||
isEqual = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
isEqual = false;
|
||||
}
|
||||
|
||||
return isEqual;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline bool List<T>::operator!=(const List<T>& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void List<T>::addElement(const T & element)
|
||||
{
|
||||
(*this)[m_Num] = element;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline const T & List<T>::operator[](size_t index) const
|
||||
{
|
||||
if (index >= m_Num)
|
||||
{
|
||||
throw fnd::Exception("List", "Out of bound read");
|
||||
}
|
||||
|
||||
return m_Vec[index];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T & List<T>::operator[](size_t index)
|
||||
{
|
||||
if (index > m_Num)
|
||||
{
|
||||
throw fnd::Exception("List", "Out of bound read");
|
||||
}
|
||||
else if (index == m_Num)
|
||||
{
|
||||
if ((m_Num * 2) >= m_Vec.size())
|
||||
{
|
||||
m_Vec.alloc((m_Num + 1) * 2);
|
||||
}
|
||||
|
||||
m_Num++;
|
||||
}
|
||||
|
||||
return m_Vec[index];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline const T & List<T>::atBack() const
|
||||
{
|
||||
return m_Vec[m_Num - 1];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T & List<T>::atBack()
|
||||
{
|
||||
return m_Vec[m_Num - 1];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline size_t List<T>::size() const
|
||||
{
|
||||
return m_Num;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void List<T>::clear()
|
||||
{
|
||||
m_Num = 0;
|
||||
m_Vec.clear();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<class K>
|
||||
inline bool List<T>::hasElement(const K & key) const
|
||||
{
|
||||
for (size_t i = 0; i < m_Num; i++)
|
||||
{
|
||||
if (m_List[i] == key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<class K>
|
||||
inline const T & List<T>::getElement(const K & key) const
|
||||
{
|
||||
for (size_t i = 0; i < m_Num; i++)
|
||||
{
|
||||
if (m_List[i] == key)
|
||||
{
|
||||
return m_List[i];
|
||||
}
|
||||
}
|
||||
|
||||
throw fnd::Exception("getElement(): element does not exist");
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<class K>
|
||||
inline T & List<T>::getElement(const K & key)
|
||||
{
|
||||
for (size_t i = 0; i < m_Num; i++)
|
||||
{
|
||||
if (m_List[i] == key)
|
||||
{
|
||||
return m_List[i];
|
||||
}
|
||||
}
|
||||
|
||||
throw fnd::Exception("getElement(): element does not exist");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
#pragma once
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#include <fnd/types.h>
|
||||
|
||||
namespace fnd
|
||||
{
|
||||
class MemoryBlob
|
||||
{
|
||||
public:
|
||||
MemoryBlob();
|
||||
MemoryBlob(const byte_t* bytes, size_t len);
|
||||
|
||||
bool operator==(const MemoryBlob& other) const;
|
||||
bool operator!=(const MemoryBlob& other) const;
|
||||
void operator=(const MemoryBlob& other);
|
||||
|
||||
void alloc(size_t size);
|
||||
void extend(size_t new_size);
|
||||
void clear();
|
||||
|
||||
inline byte_t& operator[](size_t index) { return mData[index]; }
|
||||
inline const byte_t& operator[](size_t index) const { return mData[index]; }
|
||||
|
||||
inline byte_t* getBytes() { return mData.data(); }
|
||||
inline const byte_t* getBytes() const { return mData.data(); }
|
||||
inline size_t getSize() const { return mVisableSize; }
|
||||
private:
|
||||
const std::string kModuleName = "MEMORY_BLOB";
|
||||
static const size_t kAllocBlockSize = 0x1000;
|
||||
|
||||
std::vector<byte_t> mData;
|
||||
size_t mSize;
|
||||
size_t mVisableSize;
|
||||
|
||||
void allocateMemory(size_t size);
|
||||
void clearMemory();
|
||||
};
|
||||
}
|
188
lib/libfnd/include/fnd/Vec.h
Normal file
188
lib/libfnd/include/fnd/Vec.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
#pragma once
|
||||
#include <fnd/types.h>
|
||||
|
||||
namespace fnd
|
||||
{
|
||||
template <class T>
|
||||
class Vec
|
||||
{
|
||||
public:
|
||||
// constructors
|
||||
Vec();
|
||||
Vec(const Vec<T>& other);
|
||||
Vec(const T* array, size_t num);
|
||||
~Vec();
|
||||
|
||||
// copy operator
|
||||
const Vec<T>& operator=(const Vec<T>& other);
|
||||
|
||||
// equivalence operators
|
||||
bool operator==(const Vec<T>& other) const;
|
||||
bool operator!=(const Vec<T>& other) const;
|
||||
|
||||
// element access
|
||||
const T& operator[](size_t index) const;
|
||||
T& operator[](size_t index);
|
||||
|
||||
// raw access
|
||||
const T* data() const;
|
||||
T* data();
|
||||
|
||||
// element num
|
||||
size_t size() const;
|
||||
|
||||
// allocate vector
|
||||
void alloc(size_t new_size);
|
||||
|
||||
// clear vector
|
||||
void clear();
|
||||
private:
|
||||
T * m_Vec;
|
||||
size_t m_Size;
|
||||
|
||||
void copyFrom(const T * array, size_t num);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline Vec<T>::Vec() :
|
||||
m_Vec(nullptr),
|
||||
m_Size(0)
|
||||
{}
|
||||
|
||||
template<class T>
|
||||
inline Vec<T>::Vec(const Vec<T>& other) :
|
||||
Vec()
|
||||
{
|
||||
copyFrom(other.data(), other.size());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline Vec<T>::Vec(const T * array, size_t num) :
|
||||
Vec()
|
||||
{
|
||||
copyFrom(array, num);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline Vec<T>::~Vec()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline const Vec<T>& Vec<T>::operator=(const Vec<T>& other)
|
||||
{
|
||||
copyFrom(other.data(), other.size());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline bool Vec<T>::operator==(const Vec<T>& other) const
|
||||
{
|
||||
bool isEqual = true;
|
||||
|
||||
if (m_Size == other.m_Size)
|
||||
{
|
||||
for (size_t i = 0; i < m_Size && isEqual; i++)
|
||||
{
|
||||
if (m_Vec[i] != other.m_Vec[i])
|
||||
{
|
||||
isEqual = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isEqual = false;
|
||||
}
|
||||
|
||||
return isEqual;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline bool Vec<T>::operator!=(const Vec<T>& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline const T & Vec<T>::operator[](size_t index) const
|
||||
{
|
||||
return m_Vec[index];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T & Vec<T>::operator[](size_t index)
|
||||
{
|
||||
return m_Vec[index];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline const T * Vec<T>::data() const
|
||||
{
|
||||
return m_Vec;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T * Vec<T>::data()
|
||||
{
|
||||
return m_Vec;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline size_t Vec<T>::size() const
|
||||
{
|
||||
return m_Size;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void Vec<T>::alloc(size_t new_size)
|
||||
{
|
||||
if (m_Vec != nullptr)
|
||||
{
|
||||
T* new_vec = new T[new_size];
|
||||
if (new_vec == nullptr)
|
||||
{
|
||||
fnd::Exception("Vec", "Failed to allocate memory for vector");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MIN(m_Size, new_size); i++)
|
||||
{
|
||||
new_vec[i] = m_Vec[i];
|
||||
}
|
||||
delete[] m_Vec;
|
||||
m_Vec = new_vec;
|
||||
m_Size = new_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Vec = new T[new_size];
|
||||
if (m_Vec == nullptr)
|
||||
{
|
||||
fnd::Exception("Vec", "Failed to allocate memory for vector");
|
||||
}
|
||||
m_Size = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void Vec<T>::clear()
|
||||
{
|
||||
if (m_Vec != nullptr)
|
||||
{
|
||||
delete[] m_Vec;
|
||||
}
|
||||
m_Vec = nullptr;
|
||||
m_Size = 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void Vec<T>::copyFrom(const T * array, size_t num)
|
||||
{
|
||||
clear();
|
||||
alloc(num);
|
||||
for (size_t i = 0; i < m_Size; i++)
|
||||
{
|
||||
m_Vec[i] = array[i];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
#include <fnd/MemoryBlob.h>
|
||||
|
||||
using namespace fnd;
|
||||
|
||||
MemoryBlob::MemoryBlob() :
|
||||
mData(),
|
||||
mSize(0),
|
||||
mVisableSize(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
fnd::MemoryBlob::MemoryBlob(const byte_t * bytes, size_t len) :
|
||||
mData(),
|
||||
mSize(0),
|
||||
mVisableSize(0)
|
||||
{
|
||||
alloc(len);
|
||||
memcpy(getBytes(), bytes, getSize());
|
||||
}
|
||||
|
||||
bool fnd::MemoryBlob::operator==(const MemoryBlob & other) const
|
||||
{
|
||||
bool isEqual = true;
|
||||
|
||||
if (this->getSize() == other.getSize())
|
||||
{
|
||||
isEqual = memcmp(this->getBytes(), other.getBytes(), this->getSize()) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
isEqual = false;
|
||||
}
|
||||
|
||||
|
||||
return isEqual;
|
||||
}
|
||||
|
||||
bool fnd::MemoryBlob::operator!=(const MemoryBlob & other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
void fnd::MemoryBlob::operator=(const MemoryBlob & other)
|
||||
{
|
||||
alloc(other.getSize());
|
||||
memcpy(getBytes(), other.getBytes(), getSize());
|
||||
}
|
||||
|
||||
void MemoryBlob::alloc(size_t size)
|
||||
{
|
||||
if (size > mSize)
|
||||
{
|
||||
allocateMemory(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
mVisableSize = size;
|
||||
clearMemory();
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryBlob::extend(size_t new_size)
|
||||
{
|
||||
try {
|
||||
mData.resize(new_size);
|
||||
}
|
||||
catch (...) {
|
||||
throw fnd::Exception(kModuleName, "extend() failed to allocate memory");
|
||||
}
|
||||
}
|
||||
|
||||
void fnd::MemoryBlob::clear()
|
||||
{
|
||||
mVisableSize = 0;
|
||||
}
|
||||
|
||||
void MemoryBlob::allocateMemory(size_t size)
|
||||
{
|
||||
mSize = (size_t)align(size, kAllocBlockSize);
|
||||
mVisableSize = size;
|
||||
extend(mSize);
|
||||
clearMemory();
|
||||
}
|
||||
|
||||
void MemoryBlob::clearMemory()
|
||||
{
|
||||
memset(mData.data(), 0, mSize);
|
||||
}
|
Loading…
Reference in a new issue