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

#ifndef BOTAN_SECURE_MEMORY_BUFFERS_H__
#define BOTAN_SECURE_MEMORY_BUFFERS_H__

#include <botan/allocate.h>
#include <botan/config.h>
#include <botan/mem_ops.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 same_mem(ptr(), in.ptr(), size()); }
      bool operator!=(const SecureBuffer<T,L>& in)
         { return (!(*this == in)); }

      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 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()); }

      s32bit compare(const SecureVector<T>& in) const;

      bool operator==(const SecureVector<T>& in) const
         { return (compare(in) == 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 append(const SecureVector<T>& x) { append(x.ptr(), x.size()); }

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

      void create(u32bit);
      void grow_to(u32bit) const;
      void grow_by(u32bit n) const { 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()); }
      SecureVector(const SecureVector<T>&, const SecureVector<T>&);
      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;
   };

/*************************************************
* Copy two other buffers                         *
*************************************************/
template<typename T>
SecureVector<T>::SecureVector(const SecureVector<T>& in1,
                              const SecureVector<T>& in2)
   {
   init(in1, in1.size());
   append(in2, in2.size());
   }

/*************************************************
* 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
   {
   const u32bit VECTOR_OVER_ALLOCATE = BOTAN_VECTOR_OVER_ALLOCATE;

   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);
   }

/*************************************************
* Compare this vector with another               *
*************************************************/
template<typename T>
s32bit SecureVector<T>::compare(const SecureVector<T>& in) const
   {
   const s32bit LESS_THAN = -1, EQUAL = 0, GREATER_THAN = 1;

   if(size() < in.size())
      return LESS_THAN;
   if(in.size() < size())
      return GREATER_THAN;

   for(u32bit j = 0; j != size(); j++)
      {
      if(buf[j] < in[j])
         return LESS_THAN;
      if(in[j] < buf[j])
         return GREATER_THAN;
      }
   return EQUAL;
   }

/*************************************************
* 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);
   std::swap(alloc, x.alloc);
   }

}

#endif
