/*************************************************
* Secure Memory Buffers Header File              *
* (C) 1999-2002 The Botan Project                *
*************************************************/

#ifndef BOTAN_SECURE_MEMORY_BUFFERS_H__
#define BOTAN_SECURE_MEMORY_BUFFERS_H__

#include <botan/util.h>
#include <botan/allocate.h>
#include <algorithm>

namespace Botan {

/*************************************************
* Fixed Length Memory Buffer                     *
*************************************************/
template<typename T, u32bit L>
class SecureBuffer
   {
   public:
      u32bit size() const { return L; }

      operator T* () { return buf; }
      operator const T* () const { return buf; }

      T* ptr() { return buf; }
      const T* ptr() const { return buf; }

      T* end() { return (buf + size()); }
      const T* end() const { return (buf + size()); }

      bool operator==(const SecureBuffer<T,L>& in)
         { return (cmp_mem(buf, in.buf, size()) == 0); }

      bool operator!=(const SecureBuffer<T,L>& in)
         { return (cmp_mem(buf, in.buf, size()) != 0); }

      SecureBuffer& operator=(const SecureBuffer<T,L>& in)
         { if(this != &in) copy(in, in.size()); return (*this); }

      void copy(const T in[], u32bit n = L)
         { copy_mem(buf, in, std::min(size(), n)); }
      void copy(u32bit off, const T in[], u32bit n)
         { copy_mem(buf + off, in, std::min(size() - off, n)); }
      void set(const T& in, u32bit n = L)
         { n = (n > L) ? L : n; for(u32bit j = 0; j != n; j++) buf[j] = in; }

      void clear() { clear_mem(buf, size()); }

      SecureBuffer() { init(); }
      SecureBuffer(const SecureBuffer<T, L>& in)
         { init(); copy(in.buf, in.size()); }
      SecureBuffer(const T in[], u32bit n = L) { init(); copy(in, n); }
      ~SecureBuffer()
         { alloc->deallocate(buf, sizeof(T)*L); release_allocator(alloc); }
   private:
      void init()
         { alloc = get_allocator(); buf = (T*)alloc->allocate(sizeof(T)*L); }
      SecureAllocator* alloc;
      T* buf;
   };

/*************************************************
* Variable Length Secure Memory Buffer           *
*************************************************/
template<typename T>
class SecureVector
   {
   public:
      u32bit size() const { return used; }
      u32bit is_empty() const { return (used == 0); }
      u32bit has_items() const { return (used != 0); }

      operator T* () { return buf; }
      operator const T* () const { return buf; }

      T* ptr() { return buf; }
      const T* ptr() const { return buf; }

      T* end() { return (buf + size()); }
      const T* end() const { return (buf + size()); }

      bool operator==(const SecureVector<T>& in) const
         {
         return ((size() == in.size()) &&
                 (cmp_mem(buf, in.buf, size()) == 0));
         }
      bool operator!=(const SecureVector<T>& in) const
         { return (!(*this == in)); }
      SecureVector& operator=(const SecureVector<T>& in)
         { if(this != &in) set(in, in.size()); return (*this); }

      void copy(const T in[], u32bit n)
         { copy_mem(buf, in, std::min(size(), n)); }
      void copy(u32bit off, const T in[], u32bit n)
         { copy_mem(buf + off, in, std::min(size() - off, n)); }

      void set(const T& in)
         { for(u32bit j = 0; j != size(); j++) buf[j] = in; }
      void set(const T& in, u32bit n)
         { create(n); for(u32bit j = 0; j != n; j++) buf[j] = in; }
      void set(const T in[], u32bit n)
         { create(n); copy(in, n); }

      void append(const T[], u32bit);
      void append(T x) { append(&x, 1); }

      void clear() { clear_mem(buf, allocated); }
      void destroy() { create(0); }

      void create(u32bit);
      void grow_to(u32bit) const;
      void grow_by(u32bit n) { grow_to(n + size()); }
      void resize(u32bit);
      void swap(SecureVector<T>&);

      SecureVector(u32bit n = 0) { init(); create(n); }
      SecureVector(const T in[], u32bit n) { init(in, n); }
      SecureVector(const SecureVector<T>& in) { init(in, in.size()); }
      template<u32bit L> SecureVector(const SecureBuffer<T,L>& in)
         { init(in, in.size()); }
      ~SecureVector() { deallocate(buf, allocated); release_allocator(alloc); }
   private:
      void init() { buf = 0; used = allocated = 0; alloc = get_allocator(); }
      void init(const T in[], u32bit n) { init(); set(in, n); }
      T* allocate(u32bit n) const { return (T*)alloc->allocate(sizeof(T)*n); }
      void deallocate(T* p, u32bit n) const
         { alloc->deallocate(p, sizeof(T)*n); }
      mutable T* buf;
      mutable u32bit used;
      mutable u32bit allocated;
      SecureAllocator* alloc;
   };

/*************************************************
* Create a new buffer                            *
*************************************************/
template<typename T>
void SecureVector<T>::create(u32bit n)
   {
   if(n <= allocated) { clear(); used = n; return; }
   T* old_buf = buf;
   buf = allocate(n);
   deallocate(old_buf, allocated);
   used = n;
   allocated = n;
   }

/*************************************************
* Increase the size of the buffer                *
*************************************************/
template<typename T>
void SecureVector<T>::grow_to(u32bit n) const
   {
   if(n <= used) return;
   if(n <= allocated)
      {
      clear_mem(buf + used, n - used);
      used = n;
      return;
      }
   T* new_buf = allocate(n + VECTOR_OVER_ALLOCATE);
   copy_mem(new_buf, buf, used);
   deallocate(buf, allocated);
   buf = new_buf;
   used = n;
   allocated = n + VECTOR_OVER_ALLOCATE;
   }

/*************************************************
* Resize the buffer                              *
*************************************************/
template<typename T>
void SecureVector<T>::resize(u32bit n)
   {
   if(n <= used) { used = n; return; }
   if(n <= allocated)
      {
      clear_mem(buf + used, n - used);
      used = n;
      return;
      }
   T* new_buf = allocate(n);
   copy_mem(new_buf, buf, used);
   deallocate(buf, allocated);
   buf = new_buf;
   used = n;
   allocated = n;
   }

/*************************************************
* Append bytes to this buffer                    *
*************************************************/
template<typename T>
void SecureVector<T>::append(const T data[], u32bit n)
   {
   grow_by(n);
   copy(size() - n, data, n);
   }

/*************************************************
* Swap this buffer with another one              *
*************************************************/
template<typename T>
void SecureVector<T>::swap(SecureVector<T>& x)
   {
   std::swap(buf, x.buf);
   std::swap(used, x.used);
   std::swap(allocated, x.allocated);
   }

}

#endif
