src/internals/FixString.hpp
//
//! \file
// ArduinoHttpServer
//
// Created by Sander van Woensel on 24-02-16.
// Copyright (c) 2016-2018 Sander van Woensel. All rights reserved.
//
//! Pre-allocated string
#ifndef __ArduinoHttpServer__FixString__
#define __ArduinoHttpServer__FixString__
#include <string.h>
#include <WString.h>
namespace ArduinoHttpServer
{
//------------------------------------------------------------------------------
// Class Declaration
//------------------------------------------------------------------------------
//! Fixed size string.
template <size_t MAX_SIZE>
class FixString
{
public:
constexpr static const size_t NPOS = -1;
// Constructors
explicit FixString(const char* pCStr="", size_t len=NPOS);
#ifndef ARDUINO_HTTP_SERVER_NO_FLASH
explicit FixString(const __FlashStringHelper * pFlashStr);
#endif
explicit FixString(const String& arduinoStr);
template<size_t RHS_SIZE> FixString(const FixString<RHS_SIZE>& fixStr); // Not explicit to allow for normal conversions where only size differs.
FixString(const FixString<MAX_SIZE>& fixStr) = default; // Not marked as "explicit" to allow normal return statements
explicit FixString(FixString<MAX_SIZE>&& fixStr) = default; // Move constructor
~FixString() {};
// Assignment
FixString<MAX_SIZE>& operator=(const char* pCStr);
#ifndef ARDUINO_HTTP_SERVER_NO_FLASH
FixString<MAX_SIZE>& operator=(const __FlashStringHelper * str);
#endif
FixString<MAX_SIZE>& operator=(const String& arduinostr);
template<size_t RHS_SIZE> FixString<MAX_SIZE>& operator=(const FixString<RHS_SIZE>& fixStr);
// We need this since move constructor implicitely declares assignment operators private.
FixString<MAX_SIZE>& operator=(const FixString<MAX_SIZE>& fixStr) = default;
FixString<MAX_SIZE>& operator=(FixString<MAX_SIZE>&& fixStr) = default;
// Comparison
bool operator==(const char* pRhs) const;
template<size_t RHS_SIZE> bool operator==(const FixString<RHS_SIZE>& rhs) const;
bool equalsIgnoreCase(const char* pCompareTo) const;
// Searching
int lastIndexOf(const char) const;
// Retrieval
FixString<MAX_SIZE> substring(size_t beginIndex, size_t endIndex=NPOS) const;
// Modification
FixString<MAX_SIZE>& operator+=(const char *pRhs);
#ifndef ARDUINO_HTTP_SERVER_NO_FLASH
FixString<MAX_SIZE>& operator+=(const __FlashStringHelper *pRhs);
#endif
template<size_t RHS_SIZE> FixString<MAX_SIZE>& operator+=(const FixString<RHS_SIZE>& rhs);
// Addition operators
template <size_t RHS_SIZE> FixString<MAX_SIZE> operator+(const FixString<RHS_SIZE>& rhs) const;
template <size_t RHS_SIZE> FixString<MAX_SIZE> operator+(const char* pCStr) const;
// Conversions
const char* cStr() const { return m_buffer; };
explicit operator const char*() { return m_buffer; };
String toString() const {return String(m_buffer);};
explicit operator String() const {return this->toString();};
long toInt() const { return atol(m_buffer); }
// Property retrieval
const size_t length() const { return strlen(m_buffer); };
const bool empty() const { return m_buffer[0] == '\0'; };
private:
char m_buffer[MAX_SIZE];
};
}
//------------------------------------------------------------------------------
// Class Definition
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//! \brief Construct using a character pointer.
//! \param len Number of characters to copy from _pCstr_.
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>::FixString(const char* pCStr, size_t len) :
m_buffer{0}
{
// At least one character required for the null terminator.
static_assert(MAX_SIZE >= 1, "Refuse to create a FixString with a size smaller than 1.");
if( (len != NPOS) && (len < (MAX_SIZE-1)) )
{
//len = len;
}
else
{
len = MAX_SIZE - 1;
}
// Make sure string is zero terminated when initializing.
strncpy(m_buffer, pCStr, len);
}
//------------------------------------------------------------------------------
//! \brief Construct using Flash String pointer.
#ifndef ARDUINO_HTTP_SERVER_NO_FLASH
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>::FixString(const __FlashStringHelper * pFlashStr) :
m_buffer{0}
{
strcpy_P(m_buffer, reinterpret_cast<PGM_P>(pFlashStr));
}
#endif
//------------------------------------------------------------------------------
//! \brief Construct using an Arduino String object.
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>::FixString(const String& arduinoStr) :
FixString(arduinoStr.c_str())
{
}
//------------------------------------------------------------------------------
//! \brief Construct using a different sized FixString
//! \note This creates an expensive copy. we should be able to move contruct it I guess. Also for assignment operator.
//! ... or not since m_buffer is an automatic variable so we cannot just use the one from a donor object.
template <size_t MAX_SIZE>
template <size_t RHS_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>::FixString(const FixString<RHS_SIZE>& fixStr) :
FixString(fixStr.cStr())
{
}
//------------------------------------------------------------------------------
//! \brief Assignment operator
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>& ArduinoHttpServer::FixString<MAX_SIZE>::operator=(const char* pCStr)
{
// Call default compiler generated assignment operator after constructing it.
// move-assignment operator should be able to solve the at first sight seemingly
// inefficient create+copy and copy again.
return (*this = FixString<MAX_SIZE>(pCStr));
}
//------------------------------------------------------------------------------
//! \brief Assignment operator for Flash based strings.
#ifndef ARDUINO_HTTP_SERVER_NO_FLASH
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>& ArduinoHttpServer::FixString<MAX_SIZE>::operator=(const __FlashStringHelper * pFlashStr)
{
return (*this = FixString<MAX_SIZE>(pFlashStr));
}
#endif
//------------------------------------------------------------------------------
//! \brief Assignment operator for Arduino Strings.
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>& ArduinoHttpServer::FixString<MAX_SIZE>::operator=(const String& arduinostr)
{
return (*this = FixString<MAX_SIZE>(arduinostr));
}
//------------------------------------------------------------------------------
//! \brief Assignment operator
template <size_t MAX_SIZE>
template <size_t RHS_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>& ArduinoHttpServer::FixString<MAX_SIZE>::operator=(const FixString<RHS_SIZE>& fixStr)
{
// Calls default compiler generated move assignment operator after FixString
// has been created.
return (*this = FixString<MAX_SIZE>(fixStr));
}
//------------------------------------------------------------------------------
//! \brief Compare with character pointer
template <size_t MAX_SIZE>
bool ArduinoHttpServer::FixString<MAX_SIZE>::operator==(const char* pRhs) const
{
// m_buffer is guaranteed to be zero terminated (by the constructors).
// Nevertheless copy only MAX_SIZE characters as an extra security measure.
return strncmp(m_buffer, pRhs, MAX_SIZE) == 0;
}
//------------------------------------------------------------------------------
//! \brief Compare with other FixString
template <size_t MAX_SIZE>
template <size_t MAX_RHS_SIZE>
bool ArduinoHttpServer::FixString<MAX_SIZE>::operator==(const FixString<MAX_RHS_SIZE>& rhs) const
{
return *this == rhs.cStr();
}
//------------------------------------------------------------------------------
//! \brief Compare with character pointer in a case insensitive manner.
template <size_t MAX_SIZE>
bool ArduinoHttpServer::FixString<MAX_SIZE>::equalsIgnoreCase(const char *pCompareTo) const
{
if(strlen(pCompareTo) != length())
{
return false;
}
// Lengths are equal and length is zero, so they are definately equal.
// Higher chance of not being empty but unequal size so do this
// late then the strlen test.
if(empty())
{
return true;
}
char* p1(m_buffer); // Copy pointer.
const char* p2(pCompareTo);
while (*p1)
{
if (tolower(*p1++) != tolower(*p2++))
{
return false;
}
}
return true;
}
//------------------------------------------------------------------------------
//! \brief return index of last match of ch.
template <size_t MAX_SIZE>
int ArduinoHttpServer::FixString<MAX_SIZE>::lastIndexOf(const char ch) const
{
int lastIndex(-1);
const char *pChr( strrchr(m_buffer, static_cast<int>(ch)) );
if(pChr != 0)
{
// Pointer arithmetic.
lastIndex = pChr - m_buffer;
}
return lastIndex;
}
//------------------------------------------------------------------------------
//! \brief Return a new string which consists of the part starting at index
//! _beginIndex_ till endIndex.
//! \param endIndex When omitted the string from beginIndex till the end will be returned.
//! \note This method differs from std::string::substr in that it requires an index
//! position for both the first and second parameter.
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE> ArduinoHttpServer::FixString<MAX_SIZE>::substring(size_t beginIndex, size_t endIndex) const
{
size_t len(NPOS);
if ( beginIndex > (MAX_SIZE-1) )
{
beginIndex = 0;
}
if ( endIndex > (MAX_SIZE-1) )
{
endIndex = MAX_SIZE-1;
}
if((endIndex!=NPOS) && (endIndex > beginIndex))
{
len = (endIndex - beginIndex);
}
return ArduinoHttpServer::FixString<MAX_SIZE>(m_buffer+beginIndex, len);
}
//------------------------------------------------------------------------------
//! \brief Concatenate with C string.
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>& ArduinoHttpServer::FixString<MAX_SIZE>::operator+=(const char *pRhs)
{
strncpy(m_buffer+length(), pRhs, MAX_SIZE - length() - 1);
return *this;
}
//------------------------------------------------------------------------------
//! \brief Concatenate with Flash String.
#ifndef ARDUINO_HTTP_SERVER_NO_FLASH
template <size_t MAX_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>& ArduinoHttpServer::FixString<MAX_SIZE>::operator+=(const __FlashStringHelper *pRhs)
{
strncpy_P(m_buffer+length(), (PGM_P)pRhs, MAX_SIZE - length() - 1);
return *this;
}
#endif
//------------------------------------------------------------------------------
//! \brief Concatenate FixString.
template <size_t MAX_SIZE>
template <size_t RHS_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE>& ArduinoHttpServer::FixString<MAX_SIZE>::operator+=(const FixString<RHS_SIZE>& rhs)
{
return *this += rhs.cStr();
}
//------------------------------------------------------------------------------
//! \brief Addition operator
template <size_t MAX_SIZE>
template <size_t RHS_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE> ArduinoHttpServer::FixString<MAX_SIZE>::operator+(const FixString<RHS_SIZE>& rhs) const
{
FixString<MAX_SIZE> tempString(*this);
tempString += rhs;
return tempString;
}
//------------------------------------------------------------------------------
//! \brief Addition operator
template <size_t MAX_SIZE>
template <size_t RHS_SIZE>
ArduinoHttpServer::FixString<MAX_SIZE> ArduinoHttpServer::FixString<MAX_SIZE>::operator+(const char *pCStr) const
{
FixString<MAX_SIZE> tempString(*this);
tempString += pCStr;
return tempString;
}
#endif