<commit_before>#include <cmath>
#include <Python.h>
#include "image.h"

using namespace inf;

typedef struct {
    PyObject_HEAD
    Image* image;
} ImagingObject;

extern PyTypeObject Imaging_Type;

PyObject* PyImagingNew(Image* imOut)
{
    ImagingObject* imagep;

    if (!imOut)
        return NULL;

    imagep = PyObject_New(ImagingObject, &Imaging_Type);
    if (imagep == NULL) {
        delete imOut;
        return NULL;
    }

    imagep->image = imOut;

    return (PyObject*) imagep;
}

static void _dealloc(ImagingObject* imagep)
{
    delete imagep->image;
    PyObject_Del(imagep);
}

#define PyImaging_Check(op) (Py_TYPE(op) == &Imaging_Type)

Image* PyImaging_AsImaging(PyObject *op)
{
    if (!PyImaging_Check(op)) {
        PyErr_BadInternalCall();
        return NULL;
    }

    return ((ImagingObject *)op)->image;
}

static PyObject* openImage(PyObject* self, PyObject* args)
{
    PyObject* python_file;

    if (!PyArg_ParseTuple(args, "O", &python_file))
        return NULL;

    PyObject* py_read = PyObject_GetAttrString(python_file, "read");
    if (py_read == NULL) {
        PyErr_SetString(PyExc_AttributeError,
            "That Python file object has no 'read' attribute");
        return NULL;
    }
    PyObject* read_buffer = PyObject_CallObject(py_read, NULL);

    const void* read_buffer_data;
    Py_ssize_t n_read;
    if (PyObject_AsReadBuffer(read_buffer,
        &read_buffer_data, &n_read) == -1) {
            PyErr_SetString(PyErr_Occurred(),
                "The method 'read' of the Python file object "
                "did not return a buffer.");
            return NULL;
    }

    Image* image = new Image((u8*)read_buffer_data, (u32)n_read);
    return PyImagingNew(image);
}

static PyObject* writeImageJpeg(PyObject* self, PyObject* args)
{
    PyObject* imgobj;
    PyObject* python_file;

    if (!PyArg_ParseTuple(args, "OO", &imgobj, &python_file))
        return NULL;

    Image* img = PyImaging_AsImaging(imgobj);

    PyObject* py_write = PyObject_GetAttrString(python_file, "write");
    if (py_write == NULL) {
        PyErr_SetString(PyExc_AttributeError,
            "That Python file object has no 'write' attribute");
        return NULL;
    }
    ImageCompressed* compress = ImageCompressed::Jpeg(*img, 100);
#if PY_VERSION_HEX < 0x03000000
    PyObject* bufObj = PyBuffer_FromReadWriteMemory((char*)compress->buffer(), compress->size());
#else
    PyObject* bufObj = PyMemoryView_FromMemory((char*)compress->buffer(), compress->size(), PyBUF_READ);
#endif
    PyObject* callArgs = PyTuple_New(1);
    PyTuple_SetItem(callArgs, 0, bufObj);
    PyObject_CallObject(py_write, callArgs);
    Py_DECREF(callArgs);

    delete compress;

    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject* writeImagePng(PyObject* self, PyObject* args)
{
    PyObject* imgobj;
    PyObject* python_file;

    if (!PyArg_ParseTuple(args, "OO", &imgobj, &python_file))
        return NULL;

    Image* img = PyImaging_AsImaging(imgobj);

    PyObject* py_write = PyObject_GetAttrString(python_file, "write");
    if (py_write == NULL) {
        PyErr_SetString(PyExc_AttributeError,
            "That Python file object has no 'write' attribute");
        return NULL;
    }
    ImageCompressed* compress = ImageCompressed::Png(*img);
#if PY_VERSION_HEX < 0x03000000
    PyObject* bufObj = PyBuffer_FromReadWriteMemory((char*)compress->buffer(), compress->size());
#else
    PyObject* bufObj = PyMemoryView_FromMemory((char*)compress->buffer(), compress->size(), PyBUF_READ);
#endif
    PyObject* callArgs = PyTuple_New(1);
    PyTuple_SetItem(callArgs, 0, bufObj);
    PyObject_CallObject(py_write, callArgs);
    Py_DECREF(callArgs);

    delete compress;

    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject* img_resize(PyObject* self, PyObject* args)
{
    int width;
    int height;

    if (!PyArg_ParseTuple(args, "ii", &width, &height))
        return NULL;

    Image* img = PyImaging_AsImaging(self);

    Image* newImg = img->resize(width, height);

    return PyImagingNew(newImg);
}

static PyObject* _getattr_is_ok(ImagingObject* self, void* closure)
{
    return PyBool_FromLong(self->image->isOk());
}

static PyObject* _getattr_width(ImagingObject* self, void* closure)
{
    return PyLong_FromUnsignedLong(self->image->width());
}

static PyObject* _getattr_height(ImagingObject* self, void* closure)
{
    return PyLong_FromUnsignedLong(self->image->height());
}

static PyObject* _getattr_components(ImagingObject* self, void* closure)
{
    return PyLong_FromUnsignedLong(self->image->componentCount());
}

static PyObject* _getattr_failreason(ImagingObject* self, void* closure)
{
    return PyUnicode_FromString(self->image->failureReason());
}

static struct PyGetSetDef getsetters[] = {
    { "isOk",           (getter) _getattr_is_ok },
    { "width",          (getter) _getattr_width },
    { "height",         (getter) _getattr_height },
    { "componentCount", (getter) _getattr_components },
    { "failureReason",  (getter) _getattr_failreason },
    { NULL }
};

static struct PyMethodDef methods[] = {
    { "resize", img_resize, METH_VARARGS },
    { NULL, NULL }
};

static PyTypeObject Imaging_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "Imaging",                /*tp_name*/
    sizeof(ImagingObject),    /*tp_size*/
    0,                        /*tp_itemsize*/
    /* methods */
    (destructor)_dealloc,       /*tp_dealloc*/
    0,                          /*tp_print*/
    0,                          /*tp_getattr*/
    0,                          /*tp_setattr*/
    0,                          /*tp_compare*/
    0,                          /*tp_repr*/
    0,                          /*tp_as_number */
    0,                          /*tp_as_sequence */
    0,                          /*tp_as_mapping */
    0,                          /*tp_hash*/
    0,                          /*tp_call*/
    0,                          /*tp_str*/
    0,                          /*tp_getattro*/
    0,                          /*tp_setattro*/
    0,                          /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,         /*tp_flags*/
    0,                          /*tp_doc*/
    0,                          /*tp_traverse*/
    0,                          /*tp_clear*/
    0,                          /*tp_richcompare*/
    0,                          /*tp_weaklistoffset*/
    0,                          /*tp_iter*/
    0,                          /*tp_iternext*/
    methods,                    /*tp_methods*/
    0,                          /*tp_members*/
    getsetters,                 /*tp_getset*/
};

static PyMethodDef functions[] = {
    {"openImage", (PyCFunction)openImage, METH_VARARGS},
    {"writeImageJpeg", (PyCFunction)writeImageJpeg, METH_VARARGS},
    {"writeImagePng", (PyCFunction)writeImagePng, METH_VARARGS},
    {NULL, NULL}
};

static int setup_module(PyObject* m) {
    PyObject* d = PyModule_GetDict(m);

    /* Ready object types */
    if (PyType_Ready(&Imaging_Type) < 0)
        return -1;

    return 0;
}

#if PY_VERSION_HEX >= 0x03000000
PyMODINIT_FUNC PyInit_PilLiteExt(void) {
    PyObject* m;

    static PyModuleDef module_def = {
        PyModuleDef_HEAD_INIT,
        "PilLiteExt",       /* m_name */
        NULL,               /* m_doc */
        -1,                 /* m_size */
        functions,          /* m_methods */
    };

    m = PyModule_Create(&module_def);

    if (setup_module(m) < 0)
        return NULL;

    return m;
}
#else
PyMODINIT_FUNC initPilLiteExt(void)
{
    PyObject* m = Py_InitModule("PilLiteExt", functions);
    setup_module(m);
}
#endif
<commit_msg>fix compilation<commit_after>#include <cmath>
#include <Python.h>
#include "image.h"

using namespace inf;

typedef struct {
    PyObject_HEAD
    Image* image;
} ImagingObject;

extern PyTypeObject Imaging_Type;

PyObject* PyImagingNew(Image* imOut)
{
    ImagingObject* imagep;

    if (!imOut)
        return NULL;

    imagep = PyObject_New(ImagingObject, &Imaging_Type);
    if (imagep == NULL) {
        delete imOut;
        return NULL;
    }

    imagep->image = imOut;

    return (PyObject*) imagep;
}

static void _dealloc(ImagingObject* imagep)
{
    delete imagep->image;
    PyObject_Del(imagep);
}

#define PyImaging_Check(op) (Py_TYPE(op) == &Imaging_Type)

Image* PyImaging_AsImaging(PyObject *op)
{
    if (!PyImaging_Check(op)) {
        PyErr_BadInternalCall();
        return NULL;
    }

    return ((ImagingObject *)op)->image;
}

static PyObject* openImage(PyObject* self, PyObject* args)
{
    PyObject* python_file;

    if (!PyArg_ParseTuple(args, "O", &python_file))
        return NULL;

    PyObject* py_read = PyObject_GetAttrString(python_file, "read");
    if (py_read == NULL) {
        PyErr_SetString(PyExc_AttributeError,
            "That Python file object has no 'read' attribute");
        return NULL;
    }
    PyObject* read_buffer = PyObject_CallObject(py_read, NULL);

    const void* read_buffer_data;
    Py_ssize_t n_read;
    if (PyObject_AsReadBuffer(read_buffer,
        &read_buffer_data, &n_read) == -1) {
            PyErr_SetString(PyErr_Occurred(),
                "The method 'read' of the Python file object "
                "did not return a buffer.");
            return NULL;
    }

    Image* image = new Image((u8*)read_buffer_data, (u32)n_read);
    return PyImagingNew(image);
}

static PyObject* writeImageJpeg(PyObject* self, PyObject* args)
{
    PyObject* imgobj;
    PyObject* python_file;

    if (!PyArg_ParseTuple(args, "OO", &imgobj, &python_file))
        return NULL;

    Image* img = PyImaging_AsImaging(imgobj);

    PyObject* py_write = PyObject_GetAttrString(python_file, "write");
    if (py_write == NULL) {
        PyErr_SetString(PyExc_AttributeError,
            "That Python file object has no 'write' attribute");
        return NULL;
    }
    ImageCompressed* compress = ImageCompressed::Jpeg(*img, 100);
#if PY_VERSION_HEX < 0x03000000
    PyObject* bufObj = PyBuffer_FromReadWriteMemory((char*)compress->buffer(), compress->size());
#else
    PyObject* bufObj = PyMemoryView_FromMemory((char*)compress->buffer(), compress->size(), PyBUF_READ);
#endif
    PyObject* callArgs = PyTuple_New(1);
    PyTuple_SetItem(callArgs, 0, bufObj);
    PyObject_CallObject(py_write, callArgs);
    Py_DECREF(callArgs);

    delete compress;

    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject* writeImagePng(PyObject* self, PyObject* args)
{
    PyObject* imgobj;
    PyObject* python_file;

    if (!PyArg_ParseTuple(args, "OO", &imgobj, &python_file))
        return NULL;

    Image* img = PyImaging_AsImaging(imgobj);

    PyObject* py_write = PyObject_GetAttrString(python_file, "write");
    if (py_write == NULL) {
        PyErr_SetString(PyExc_AttributeError,
            "That Python file object has no 'write' attribute");
        return NULL;
    }
    ImageCompressed* compress = ImageCompressed::Png(*img);
#if PY_VERSION_HEX < 0x03000000
    PyObject* bufObj = PyBuffer_FromReadWriteMemory((char*)compress->buffer(), compress->size());
#else
    PyObject* bufObj = PyMemoryView_FromMemory((char*)compress->buffer(), compress->size(), PyBUF_READ);
#endif
    PyObject* callArgs = PyTuple_New(1);
    PyTuple_SetItem(callArgs, 0, bufObj);
    PyObject_CallObject(py_write, callArgs);
    Py_DECREF(callArgs);

    delete compress;

    Py_INCREF(Py_None);
    return Py_None;
}

static PyObject* img_resize(PyObject* self, PyObject* args)
{
    int width;
    int height;

    if (!PyArg_ParseTuple(args, "ii", &width, &height))
        return NULL;

    Image* img = PyImaging_AsImaging(self);

    Image* newImg = img->resize(width, height);

    return PyImagingNew(newImg);
}

static PyObject* _getattr_is_ok(ImagingObject* self, void* closure)
{
    return PyBool_FromLong(self->image->isOk());
}

static PyObject* _getattr_width(ImagingObject* self, void* closure)
{
    return PyLong_FromUnsignedLong(self->image->width());
}

static PyObject* _getattr_height(ImagingObject* self, void* closure)
{
    return PyLong_FromUnsignedLong(self->image->height());
}

static PyObject* _getattr_components(ImagingObject* self, void* closure)
{
    return PyLong_FromUnsignedLong(self->image->componentCount());
}

static PyObject* _getattr_failreason(ImagingObject* self, void* closure)
{
    return PyUnicode_FromString(self->image->failureReason());
}

static struct PyGetSetDef getsetters[] = {
    { "isOk",           (getter) _getattr_is_ok },
    { "width",          (getter) _getattr_width },
    { "height",         (getter) _getattr_height },
    { "componentCount", (getter) _getattr_components },
    { "failureReason",  (getter) _getattr_failreason },
    { NULL }
};

static struct PyMethodDef methods[] = {
    { "resize", img_resize, METH_VARARGS },
    { NULL, NULL }
};

PyTypeObject Imaging_Type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "Imaging",                /*tp_name*/
    sizeof(ImagingObject),    /*tp_size*/
    0,                        /*tp_itemsize*/
    /* methods */
    (destructor)_dealloc,       /*tp_dealloc*/
    0,                          /*tp_print*/
    0,                          /*tp_getattr*/
    0,                          /*tp_setattr*/
    0,                          /*tp_compare*/
    0,                          /*tp_repr*/
    0,                          /*tp_as_number */
    0,                          /*tp_as_sequence */
    0,                          /*tp_as_mapping */
    0,                          /*tp_hash*/
    0,                          /*tp_call*/
    0,                          /*tp_str*/
    0,                          /*tp_getattro*/
    0,                          /*tp_setattro*/
    0,                          /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT,         /*tp_flags*/
    0,                          /*tp_doc*/
    0,                          /*tp_traverse*/
    0,                          /*tp_clear*/
    0,                          /*tp_richcompare*/
    0,                          /*tp_weaklistoffset*/
    0,                          /*tp_iter*/
    0,                          /*tp_iternext*/
    methods,                    /*tp_methods*/
    0,                          /*tp_members*/
    getsetters,                 /*tp_getset*/
};

static PyMethodDef functions[] = {
    {"openImage", (PyCFunction)openImage, METH_VARARGS},
    {"writeImageJpeg", (PyCFunction)writeImageJpeg, METH_VARARGS},
    {"writeImagePng", (PyCFunction)writeImagePng, METH_VARARGS},
    {NULL, NULL}
};

static int setup_module(PyObject* m) {
    PyObject* d = PyModule_GetDict(m);

    /* Ready object types */
    if (PyType_Ready(&Imaging_Type) < 0)
        return -1;

    return 0;
}

#if PY_VERSION_HEX >= 0x03000000
PyMODINIT_FUNC PyInit_PilLiteExt(void) {
    PyObject* m;

    static PyModuleDef module_def = {
        PyModuleDef_HEAD_INIT,
        "PilLiteExt",       /* m_name */
        NULL,               /* m_doc */
        -1,                 /* m_size */
        functions,          /* m_methods */
    };

    m = PyModule_Create(&module_def);

    if (setup_module(m) < 0)
        return NULL;

    return m;
}
#else
PyMODINIT_FUNC initPilLiteExt(void)
{
    PyObject* m = Py_InitModule("PilLiteExt", functions);
    setup_module(m);
}
#endif
<|endoftext|>


<commit_before>/*
 * This file is part of the µOS++ distribution.
 *   (https://github.com/micro-os-plus)
 * Copyright (c) 2016 Liviu Ionescu.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <cmsis-plus/memory/first-fit-top.h>
#include <memory>

// ----------------------------------------------------------------------------

namespace os
{
  namespace memory
  {

    // ========================================================================

    /**
     * @details
     */
    first_fit_top::~first_fit_top ()
    {
      trace::printf ("%s() @%p %s\n", __func__, this, name ());
    }

    /**
     * @details
     */
    void
    first_fit_top::internal_construct_ (void* addr, std::size_t bytes)
    {
      assert(bytes > chunk_minsize);

      arena_addr_ = addr;
      total_bytes_ = bytes;

      // Align address for first chunk.
      void* res;
      // Possibly adjust the last two parameters.
      res = std::align (chunk_align, chunk_minsize, arena_addr_, total_bytes_);
      // std::align() will fail if it cannot fit the min chunk.
      assert(res != nullptr);
      assert((total_bytes_ % chunk_align) == 0);

      internal_reset_ ();
    }

    /**
     * @details
     */
    void
    first_fit_top::internal_reset_ (void) noexcept
    {
      // Fill it with the first chunk.
      chunk_t* chunk = reinterpret_cast<chunk_t*> (arena_addr_);
      // Entire arena is a big free chunk.
      chunk->size = total_bytes_;
      // Mark the end of the list with a null pointer.
      chunk->next = nullptr;

      allocated_bytes_ = 0;
      max_allocated_bytes_ = 0;
      free_bytes_ = total_bytes_;
      allocated_chunks_ = 0;
      free_chunks_ = 1;

      // Remember first chunk as list head.
      free_list_ = chunk;
    }

    /**
     * @details
     */
    void
    first_fit_top::do_reset (void) noexcept
    {
#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
      trace::printf ("%s() @%p %s\n", __func__, this, name ());
#endif

      internal_reset_ ();
    }

#pragma GCC diagnostic push
// Needed because 'alignment' is used only in trace calls.
#pragma GCC diagnostic ignored "-Wunused-parameter"

    /**
     * @details
     * The allocator tries to be fast and grasps the first block
     * large enough, possibly splitting large blocks and increasing
     * fragmentation. If the block is only slightly larger
     * (the remaining space is not large enough for a minimum chunk)
     * the block is not split, but left partly unused.
     *
     * When large blocks are split, the top sub-block is returned;
     * in other words, memory is allocated top-down. This speeds
     * up deallocation for blocks allocated recently.
     *
     * @par Exceptions
     *   Throws nothing by itself, but the out of memory handler may
     *   throw `bad_alloc()`.
     */
    void*
    first_fit_top::do_allocate (std::size_t bytes, std::size_t alignment)
    {
      std::size_t block_padding = calc_block_padding (alignment);
      std::size_t alloc_size = rtos::memory::align_size (bytes, chunk_align);
      alloc_size += block_padding;
      alloc_size += chunk_offset;

      std::size_t block_minchunk = calc_block_minchunk (block_padding);
      alloc_size = os::rtos::memory::max (alloc_size, block_minchunk);

      chunk_t* chunk;

      while (true)
        {
          chunk_t* prev_chunk = free_list_;
          chunk = prev_chunk;

          while (chunk)
            {
              int rem = static_cast<int> (chunk->size - alloc_size);
              if (rem >= 0)
                {
                  if ((static_cast<std::size_t> (rem)) >= block_minchunk)
                    {
                      // Found a chunk that is much larger than required size
                      // (at least one more chunk is available);
                      // break it into two chunks and return the second one.

                      chunk->size = static_cast<std::size_t> (rem);
                      chunk =
                          reinterpret_cast<chunk_t *> (reinterpret_cast<char *> (chunk)
                              + rem);
                      chunk->size = alloc_size;

                      // Splitting one chunk creates one more chunk.
                      ++free_chunks_;
                    }
                  else
                    {
                      // Found a chunk that is exactly the size or slightly
                      // larger than the requested size; return this chunk.

                      if (prev_chunk == chunk)
                        {
                          // This implies p==r==free_list, i.e. the list head.
                          // The next chunk becomes the first list element.
                          free_list_ = chunk->next;

                          // If this was the last chunk, the free list is empty.
                        }
                      else
                        {
                          // Normal case. Remove it from the free_list.
                          prev_chunk->next = chunk->next;
                        }
                    }
                  break;
                }
              prev_chunk = chunk;
              chunk = chunk->next;
            }

          if (chunk != nullptr)
            {
              break;
            }

          if (out_of_memory_handler_ == nullptr)
            {
#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
              trace::printf ("%s(%u,%u)=0 @%p %s\n", __func__, bytes, alignment,
                             this, name ());
#endif

              return nullptr;
            }

#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
          trace::printf ("%s(%u,%u) @%p %s out of memory\n", __func__, bytes,
                         alignment, this, name ());
#endif
          out_of_memory_handler_ ();

          // If the handler returned, assume it freed some memory
          // and try again to allocate.
        }

      // Update statistics.
      // The value subtracted from free is added to allocated.
      internal_increase_allocated_statistics (chunk->size);

      // Compute pointer to payload area.
      char* payload = reinterpret_cast<char *> (chunk) + chunk_offset;

      // Align it to user provided alignment.
      void* aligned_payload = payload;
      std::size_t aligned_size = chunk->size - chunk_offset;

      void* res;
      res = std::align (alignment, bytes, aligned_payload, aligned_size);
      assert(res != nullptr);

      // Compute the possible alignment offset.
      std::ptrdiff_t offset = static_cast<char *> (aligned_payload) - payload;
      if (offset)
        {
          // If non-zero, store it in the gap left by alignment in the
          // chunk header.

          chunk_t* adj_chunk =
              reinterpret_cast<chunk_t *> (static_cast<char *> (aligned_payload)
                  - chunk_offset);
          adj_chunk->size = static_cast<std::size_t> (-offset);
        }

#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
      trace::printf ("%s(%u,%u)=%p,%u @%p %s\n", __func__, bytes, alignment,
                     aligned_payload, alloc_size, this, name ());
#endif

      return aligned_payload;
    }

    /**
     * @details
     *
     * Deallocation is not guaranteed to be deterministic, but if
     * done in strict reverse allocation order, it becomes deterministic,
     * otherwise a traversal of the free list is required, the older the
     * block, the more nodes to traverse (the free list is kept in
     * ascending addresses order).
     *
     * If the block is already in the free list, issue a trace message,
     * but otherwise ignore the condition.
     *
     * @par Exceptions
     *   Throws nothing.
     */
    void
    first_fit_top::do_deallocate (void* addr, std::size_t bytes,
                                  std::size_t alignment) noexcept
    {
#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
      trace::printf ("%s(%p,%u,%u) @%p %s\n", __func__, addr, bytes, alignment,
                     this, name ());
#endif

      // The address must be inside the arena; no exceptions.
      if ((addr < arena_addr_)
          || (addr > (static_cast<char*> (arena_addr_) + total_bytes_)))
        {
          assert(false);
          return;
        }

      // Compute the chunk address from the user address.
      chunk_t* chunk = reinterpret_cast<chunk_t *> (static_cast<char *> (addr)
          - chunk_offset);

      // If the block was aligned, the offset appears as size; adjust back.
      if (static_cast<std::ptrdiff_t> (chunk->size) < 0)
        {
          chunk = reinterpret_cast<chunk_t *> (reinterpret_cast<char *> (chunk)
              + static_cast<std::ptrdiff_t> (chunk->size));
        }

      if (bytes)
        {
          // If size is known, validate.
          // (when called from free(), the size is not known).
          if (bytes + chunk_offset > chunk->size)
            {
              assert(false);
              return;
            }
        }

      // Update statistics.
      // What is subtracted from allocated is added to free.
      internal_decrease_allocated_statistics (chunk->size);

      // If the free list is empty, create it with the current chunk, alone.
      if (free_list_ == nullptr)
        {
          // Mark the end of the list with a null pointer.
          chunk->next = nullptr;

          // The chunk becomes the first list element.
          free_list_ = chunk;
          assert(free_chunks_ == 1);

          return;
        }

      // The free list exists; is the chunk before the list head?
      if (chunk < free_list_)
        {
          // Is the chunk *right* before the list head?
          if (reinterpret_cast<char *> (chunk) + chunk->size
              == reinterpret_cast<char *> (free_list_))
            {
              // Coalesce chunk to the list head.
              chunk->size += free_list_->size;
              chunk->next = free_list_->next; // May be nullptr

              // Coalescing means one less chunk.
              --free_chunks_;
            }
          else
            {
              // Insert before the list head.
              chunk->next = free_list_;
            }
          // The chunk becomes the new list head.
          free_list_ = chunk;

          return;
        }

      // Walk the free list to find the place to insert,
      // (the list must be ordered by addresses).
      // Warning: not deterministic!

      chunk_t* next_chunk = free_list_;
      chunk_t* prev_chunk;
      do
        {
          prev_chunk = next_chunk;
          next_chunk = next_chunk->next;
        }
      while (next_chunk != nullptr && next_chunk <= chunk);

      // Now prev_chunk <= chunk and either next_chunk == nullptr or
      // next_chunk > chunk.
      // Try to merge with chunks immediately before/after it.

      if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
          == reinterpret_cast<char *> (chunk))
        {
          // Chunk to be freed is adjacent to a free chunk before it.
          prev_chunk->size += chunk->size;

          // Coalescing means one less chunk.
          --free_chunks_;

          // If the merged chunk is also adjacent to the chunk after it,
          // merge again. Does not match if next_chunk == nullptr.
          if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
              == reinterpret_cast<char *> (next_chunk))
            {
              prev_chunk->size += next_chunk->size;
              prev_chunk->next = next_chunk->next;

              // Coalescing means one less chunk.
              --free_chunks_;
            }
        }
      else if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
          > reinterpret_cast<char *> (chunk))
        {
          // Already freed.

          // Revert statistics.
          // What is subtracted from free is added to allocated.
          allocated_bytes_ += chunk->size;
          free_bytes_ -= chunk->size;
          ++allocated_chunks_;
          --free_chunks_;

          trace::printf ("%s(%p,%u,%u) @%p %s already freed\n", __func__, addr,
                         bytes, alignment, this, name ());

          return;
        }
      // Does not match if next_chunk == nullptr.
      else if (reinterpret_cast<char *> (chunk) + chunk->size
          == reinterpret_cast<char *> (next_chunk))
        {
          // The chunk to be freed is adjacent to a free chunk after it.
          chunk->size += next_chunk->size;
          chunk->next = next_chunk->next; // May be nullptr.
          prev_chunk->next = chunk;

          // Coalescing means one less chunk.
          --free_chunks_;
        }
      else
        {
          // Not adjacent to any chunk. Just insert it.
          // The result is a new fragment. Not great...
          chunk->next = next_chunk; // May be nullptr.
          prev_chunk->next = chunk;
        }
    }

    /**
     * @details
     */
    std::size_t
    first_fit_top::do_max_size (void) const noexcept
    {
      return total_bytes_;
    }

#pragma GCC diagnostic pop

  // --------------------------------------------------------------------------
  } /* namespace memory */
} /* namespace os */

// ----------------------------------------------------------------------------
<commit_msg>memory/first-fit-top: fix warnings<commit_after>/*
 * This file is part of the µOS++ distribution.
 *   (https://github.com/micro-os-plus)
 * Copyright (c) 2016 Liviu Ionescu.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <cmsis-plus/memory/first-fit-top.h>
#include <memory>

// ----------------------------------------------------------------------------

namespace os
{
  namespace memory
  {

    // ========================================================================

    /**
     * @details
     */
    first_fit_top::~first_fit_top ()
    {
      trace::printf ("%s() @%p %s\n", __func__, this, name ());
    }

    /**
     * @details
     */
    void
    first_fit_top::internal_construct_ (void* addr, std::size_t bytes)
    {
      assert(bytes > chunk_minsize);

      arena_addr_ = addr;
      total_bytes_ = bytes;

      // Align address for first chunk.
      void* res;
      // Possibly adjust the last two parameters.
      res = std::align (chunk_align, chunk_minsize, arena_addr_, total_bytes_);
      // std::align() will fail if it cannot fit the min chunk.
      if (res == nullptr)
        {
          assert(res != nullptr);
        }
      assert((total_bytes_ % chunk_align) == 0);

      internal_reset_ ();
    }

    /**
     * @details
     */
    void
    first_fit_top::internal_reset_ (void) noexcept
    {
      // Fill it with the first chunk.
      chunk_t* chunk = reinterpret_cast<chunk_t*> (arena_addr_);
      // Entire arena is a big free chunk.
      chunk->size = total_bytes_;
      // Mark the end of the list with a null pointer.
      chunk->next = nullptr;

      allocated_bytes_ = 0;
      max_allocated_bytes_ = 0;
      free_bytes_ = total_bytes_;
      allocated_chunks_ = 0;
      free_chunks_ = 1;

      // Remember first chunk as list head.
      free_list_ = chunk;
    }

    /**
     * @details
     */
    void
    first_fit_top::do_reset (void) noexcept
    {
#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
      trace::printf ("%s() @%p %s\n", __func__, this, name ());
#endif

      internal_reset_ ();
    }

#pragma GCC diagnostic push
// Needed because 'alignment' is used only in trace calls.
#pragma GCC diagnostic ignored "-Wunused-parameter"

    /**
     * @details
     * The allocator tries to be fast and grasps the first block
     * large enough, possibly splitting large blocks and increasing
     * fragmentation. If the block is only slightly larger
     * (the remaining space is not large enough for a minimum chunk)
     * the block is not split, but left partly unused.
     *
     * When large blocks are split, the top sub-block is returned;
     * in other words, memory is allocated top-down. This speeds
     * up deallocation for blocks allocated recently.
     *
     * @par Exceptions
     *   Throws nothing by itself, but the out of memory handler may
     *   throw `bad_alloc()`.
     */
    void*
    first_fit_top::do_allocate (std::size_t bytes, std::size_t alignment)
    {
      std::size_t block_padding = calc_block_padding (alignment);
      std::size_t alloc_size = rtos::memory::align_size (bytes, chunk_align);
      alloc_size += block_padding;
      alloc_size += chunk_offset;

      std::size_t block_minchunk = calc_block_minchunk (block_padding);
      alloc_size = os::rtos::memory::max (alloc_size, block_minchunk);

      chunk_t* chunk;

      while (true)
        {
          chunk_t* prev_chunk = free_list_;
          chunk = prev_chunk;

          while (chunk)
            {
              int rem = static_cast<int> (chunk->size - alloc_size);
              if (rem >= 0)
                {
                  if ((static_cast<std::size_t> (rem)) >= block_minchunk)
                    {
                      // Found a chunk that is much larger than required size
                      // (at least one more chunk is available);
                      // break it into two chunks and return the second one.

                      chunk->size = static_cast<std::size_t> (rem);
                      chunk =
                          reinterpret_cast<chunk_t *> (reinterpret_cast<char *> (chunk)
                              + rem);
                      chunk->size = alloc_size;

                      // Splitting one chunk creates one more chunk.
                      ++free_chunks_;
                    }
                  else
                    {
                      // Found a chunk that is exactly the size or slightly
                      // larger than the requested size; return this chunk.

                      if (prev_chunk == chunk)
                        {
                          // This implies p==r==free_list, i.e. the list head.
                          // The next chunk becomes the first list element.
                          free_list_ = chunk->next;

                          // If this was the last chunk, the free list is empty.
                        }
                      else
                        {
                          // Normal case. Remove it from the free_list.
                          prev_chunk->next = chunk->next;
                        }
                    }
                  break;
                }
              prev_chunk = chunk;
              chunk = chunk->next;
            }

          if (chunk != nullptr)
            {
              break;
            }

          if (out_of_memory_handler_ == nullptr)
            {
#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
              trace::printf ("%s(%u,%u)=0 @%p %s\n", __func__, bytes, alignment,
                             this, name ());
#endif

              return nullptr;
            }

#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
          trace::printf ("%s(%u,%u) @%p %s out of memory\n", __func__, bytes,
                         alignment, this, name ());
#endif
          out_of_memory_handler_ ();

          // If the handler returned, assume it freed some memory
          // and try again to allocate.
        }

      // Update statistics.
      // The value subtracted from free is added to allocated.
      internal_increase_allocated_statistics (chunk->size);

      // Compute pointer to payload area.
      char* payload = reinterpret_cast<char *> (chunk) + chunk_offset;

      // Align it to user provided alignment.
      void* aligned_payload = payload;
      std::size_t aligned_size = chunk->size - chunk_offset;

      void* res;
      res = std::align (alignment, bytes, aligned_payload, aligned_size);
      if (res == nullptr)
        {
          assert(res != nullptr);
        }

      // Compute the possible alignment offset.
      std::ptrdiff_t offset = static_cast<char *> (aligned_payload) - payload;
      if (offset)
        {
          // If non-zero, store it in the gap left by alignment in the
          // chunk header.

          chunk_t* adj_chunk =
              reinterpret_cast<chunk_t *> (static_cast<char *> (aligned_payload)
                  - chunk_offset);
          adj_chunk->size = static_cast<std::size_t> (-offset);
        }

#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
      trace::printf ("%s(%u,%u)=%p,%u @%p %s\n", __func__, bytes, alignment,
                     aligned_payload, alloc_size, this, name ());
#endif

      return aligned_payload;
    }

    /**
     * @details
     *
     * Deallocation is not guaranteed to be deterministic, but if
     * done in strict reverse allocation order, it becomes deterministic,
     * otherwise a traversal of the free list is required, the older the
     * block, the more nodes to traverse (the free list is kept in
     * ascending addresses order).
     *
     * If the block is already in the free list, issue a trace message,
     * but otherwise ignore the condition.
     *
     * @par Exceptions
     *   Throws nothing.
     */
    void
    first_fit_top::do_deallocate (void* addr, std::size_t bytes,
                                  std::size_t alignment) noexcept
    {
#if defined(OS_TRACE_LIBCPP_MEMORY_RESOURCE)
      trace::printf ("%s(%p,%u,%u) @%p %s\n", __func__, addr, bytes, alignment,
                     this, name ());
#endif

      // The address must be inside the arena; no exceptions.
      if ((addr < arena_addr_)
          || (addr > (static_cast<char*> (arena_addr_) + total_bytes_)))
        {
          assert(false);
          return;
        }

      // Compute the chunk address from the user address.
      chunk_t* chunk = reinterpret_cast<chunk_t *> (static_cast<char *> (addr)
          - chunk_offset);

      // If the block was aligned, the offset appears as size; adjust back.
      if (static_cast<std::ptrdiff_t> (chunk->size) < 0)
        {
          chunk = reinterpret_cast<chunk_t *> (reinterpret_cast<char *> (chunk)
              + static_cast<std::ptrdiff_t> (chunk->size));
        }

      if (bytes)
        {
          // If size is known, validate.
          // (when called from free(), the size is not known).
          if (bytes + chunk_offset > chunk->size)
            {
              assert(false);
              return;
            }
        }

      // Update statistics.
      // What is subtracted from allocated is added to free.
      internal_decrease_allocated_statistics (chunk->size);

      // If the free list is empty, create it with the current chunk, alone.
      if (free_list_ == nullptr)
        {
          // Mark the end of the list with a null pointer.
          chunk->next = nullptr;

          // The chunk becomes the first list element.
          free_list_ = chunk;
          assert(free_chunks_ == 1);

          return;
        }

      // The free list exists; is the chunk before the list head?
      if (chunk < free_list_)
        {
          // Is the chunk *right* before the list head?
          if (reinterpret_cast<char *> (chunk) + chunk->size
              == reinterpret_cast<char *> (free_list_))
            {
              // Coalesce chunk to the list head.
              chunk->size += free_list_->size;
              chunk->next = free_list_->next; // May be nullptr

              // Coalescing means one less chunk.
              --free_chunks_;
            }
          else
            {
              // Insert before the list head.
              chunk->next = free_list_;
            }
          // The chunk becomes the new list head.
          free_list_ = chunk;

          return;
        }

      // Walk the free list to find the place to insert,
      // (the list must be ordered by addresses).
      // Warning: not deterministic!

      chunk_t* next_chunk = free_list_;
      chunk_t* prev_chunk;
      do
        {
          prev_chunk = next_chunk;
          next_chunk = next_chunk->next;
        }
      while (next_chunk != nullptr && next_chunk <= chunk);

      // Now prev_chunk <= chunk and either next_chunk == nullptr or
      // next_chunk > chunk.
      // Try to merge with chunks immediately before/after it.

      if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
          == reinterpret_cast<char *> (chunk))
        {
          // Chunk to be freed is adjacent to a free chunk before it.
          prev_chunk->size += chunk->size;

          // Coalescing means one less chunk.
          --free_chunks_;

          // If the merged chunk is also adjacent to the chunk after it,
          // merge again. Does not match if next_chunk == nullptr.
          if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
              == reinterpret_cast<char *> (next_chunk))
            {
              prev_chunk->size += next_chunk->size;
              prev_chunk->next = next_chunk->next;

              // Coalescing means one less chunk.
              --free_chunks_;
            }
        }
      else if (reinterpret_cast<char *> (prev_chunk) + prev_chunk->size
          > reinterpret_cast<char *> (chunk))
        {
          // Already freed.

          // Revert statistics.
          // What is subtracted from free is added to allocated.
          allocated_bytes_ += chunk->size;
          free_bytes_ -= chunk->size;
          ++allocated_chunks_;
          --free_chunks_;

          trace::printf ("%s(%p,%u,%u) @%p %s already freed\n", __func__, addr,
                         bytes, alignment, this, name ());

          return;
        }
      // Does not match if next_chunk == nullptr.
      else if (reinterpret_cast<char *> (chunk) + chunk->size
          == reinterpret_cast<char *> (next_chunk))
        {
          // The chunk to be freed is adjacent to a free chunk after it.
          chunk->size += next_chunk->size;
          chunk->next = next_chunk->next; // May be nullptr.
          prev_chunk->next = chunk;

          // Coalescing means one less chunk.
          --free_chunks_;
        }
      else
        {
          // Not adjacent to any chunk. Just insert it.
          // The result is a new fragment. Not great...
          chunk->next = next_chunk; // May be nullptr.
          prev_chunk->next = chunk;
        }
    }

    /**
     * @details
     */
    std::size_t
    first_fit_top::do_max_size (void) const noexcept
    {
      return total_bytes_;
    }

#pragma GCC diagnostic pop

  // --------------------------------------------------------------------------
  } /* namespace memory */
} /* namespace os */

// ----------------------------------------------------------------------------
<|endoftext|>


<commit_before>// Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "intro.h"
#include "ui_intro.h"

#include "util.h"

#include <boost/filesystem.hpp>
#include <QFileDialog>
#include <QSettings>
#include <QMessageBox>

/* Minimum free space (in bytes) needed for data directory */
static const uint64_t GB_BYTES = 1000000000LL;
static const uint64_t BLOCK_CHAIN_SIZE = 20LL * GB_BYTES;

/* Check free space asynchronously to prevent hanging the UI thread.

   Up to one request to check a path is in flight to this thread; when the check()
   function runs, the current path is requested from the associated Intro object.
   The reply is sent back through a signal.

   This ensures that no queue of checking requests is built up while the user is
   still entering the path, and that always the most recently entered path is checked as
   soon as the thread becomes available.
*/
class FreespaceChecker : public QObject
{
    Q_OBJECT

public:
    FreespaceChecker(Intro *intro);

    enum Status {
        ST_OK,
        ST_ERROR
    };

public slots:
    void check();

signals:
    void reply(int status, const QString &message, quint64 available);

private:
    Intro *intro;
};

#include "intro.moc"

FreespaceChecker::FreespaceChecker(Intro *intro)
{
    this->intro = intro;
}

void FreespaceChecker::check()
{
    namespace fs = boost::filesystem;
    QString dataDirStr = intro->getPathToCheck();
    fs::path dataDir = fs::path(dataDirStr.toStdString());
    uint64_t freeBytesAvailable = 0;
    int replyStatus = ST_OK;
    QString replyMessage = tr("A new data directory will be created.");

    /* Find first parent that exists, so that fs::space does not fail */
    fs::path parentDir = dataDir;
    fs::path parentDirOld = fs::path();
    while(parentDir.has_parent_path() && !fs::exists(parentDir))
    {
        parentDir = parentDir.parent_path();

        /* Check if we make any progress, break if not to prevent an infinite loop here */
        if (parentDirOld == parentDir)
            break;

        parentDirOld = parentDir;
    }

    try {
        freeBytesAvailable = fs::space(parentDir).available;
        if(fs::exists(dataDir))
        {
            if(fs::is_directory(dataDir))
            {
                QString separator = "" + QDir::toNativeSeparators("/") + tr("name") + "";
                replyStatus = ST_OK;
                replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
            } else {
                replyStatus = ST_ERROR;
                replyMessage = tr("Path already exists, and is not a directory.");
            }
        }
    } catch(fs::filesystem_error &e)
    {
        /* Parent directory does not exist or is not accessible */
        replyStatus = ST_ERROR;
        replyMessage = tr("Cannot create data directory here.");
    }
    emit reply(replyStatus, replyMessage, freeBytesAvailable);
}

Intro::Intro(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Intro),
    thread(0),
    signalled(false)
{
    ui->setupUi(this);
    ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES));
    startThread();
}

Intro::~Intro()
{
    delete ui;
    /* Ensure thread is finished before it is deleted */
    emit stopThread();
    thread->wait();
}

QString Intro::getDataDirectory()
{
    return ui->dataDirectory->text();
}

void Intro::setDataDirectory(const QString &dataDir)
{
    ui->dataDirectory->setText(dataDir);
    if(dataDir == getDefaultDataDirectory())
    {
        ui->dataDirDefault->setChecked(true);
        ui->dataDirectory->setEnabled(false);
        ui->ellipsisButton->setEnabled(false);
    } else {
        ui->dataDirCustom->setChecked(true);
        ui->dataDirectory->setEnabled(true);
        ui->ellipsisButton->setEnabled(true);
    }
}

QString Intro::getDefaultDataDirectory()
{
    return QString::fromStdString(GetDefaultDataDir().string());
}

void Intro::pickDataDirectory()
{
    namespace fs = boost::filesystem;
    QSettings settings;
    /* If data directory provided on command line, no need to look at settings
       or show a picking dialog */
    if(!GetArg("-datadir", "").empty())
        return;
    /* 1) Default data directory for operating system */
    QString dataDir = getDefaultDataDirectory();
    /* 2) Allow QSettings to override default dir */
    dataDir = settings.value("strDataDir", dataDir).toString();

    if(!fs::exists(dataDir.toStdString()) || GetBoolArg("-choosedatadir", false))
    {
        /* If current default data directory does not exist, let the user choose one */
        Intro intro;
        intro.setDataDirectory(dataDir);
        intro.setWindowIcon(QIcon(":icons/bitcoin"));

        while(true)
        {
            if(!intro.exec())
            {
                /* Cancel clicked */
                exit(0);
            }
            dataDir = intro.getDataDirectory();
            try {
                fs::create_directory(dataDir.toStdString());
                break;
            } catch(fs::filesystem_error &e) {
                QMessageBox::critical(0, tr("Bitcoin"),
                    tr("Error: Specified data directory \"%1\" can not be created.").arg(dataDir));
                /* fall through, back to choosing screen */
            }
        }

        settings.setValue("strDataDir", dataDir);
    }
    SoftSetArg("-datadir", dataDir.toStdString());
}

void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
{
    switch(status)
    {
    case FreespaceChecker::ST_OK:
        ui->errorMessage->setText(message);
        ui->errorMessage->setStyleSheet("");
        break;
    case FreespaceChecker::ST_ERROR:
        ui->errorMessage->setText(tr("Error") + ": " + message);
        ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
        break;
    }
    /* Indicate number of bytes available */
    if(status == FreespaceChecker::ST_ERROR)
    {
        ui->freeSpace->setText("");
    } else {
        QString freeString = QString::number(bytesAvailable/GB_BYTES) + tr("GB of free space available");
        if(bytesAvailable < BLOCK_CHAIN_SIZE)
        {
            freeString += " " + tr("(of %1GB needed)").arg(BLOCK_CHAIN_SIZE/GB_BYTES);
            ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
        } else {
            ui->freeSpace->setStyleSheet("");
        }
        ui->freeSpace->setText(freeString + ".");
    }
    /* Don't allow confirm in ERROR state */
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
}

void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
{
    /* Disable OK button until check result comes in */
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
    checkPath(dataDirStr);
}

void Intro::on_ellipsisButton_clicked()
{
    QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
    if(!dir.isEmpty())
        ui->dataDirectory->setText(dir);
}

void Intro::on_dataDirDefault_clicked()
{
    setDataDirectory(getDefaultDataDirectory());
}

void Intro::on_dataDirCustom_clicked()
{
    ui->dataDirectory->setEnabled(true);
    ui->ellipsisButton->setEnabled(true);
}

void Intro::startThread()
{
    thread = new QThread(this);
    FreespaceChecker *executor = new FreespaceChecker(this);
    executor->moveToThread(thread);

    connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
    connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
    /*  make sure executor object is deleted in its own thread */
    connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
    connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));

    thread->start();
}

void Intro::checkPath(const QString &dataDir)
{
    mutex.lock();
    pathToCheck = dataDir;
    if(!signalled)
    {
        signalled = true;
        emit requestCheck();
    }
    mutex.unlock();
}

QString Intro::getPathToCheck()
{
    QString retval;
    mutex.lock();
    retval = pathToCheck;
    signalled = false; /* new request can be queued now */
    mutex.unlock();
    return retval;
}
<commit_msg>decrease minimum blockchain size to current dogecoin level<commit_after>// Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "intro.h"
#include "ui_intro.h"

#include "util.h"

#include <boost/filesystem.hpp>
#include <QFileDialog>
#include <QSettings>
#include <QMessageBox>

/* Minimum free space (in bytes) needed for data directory */
static const uint64_t GB_BYTES = 1000000000LL;
static const uint64_t BLOCK_CHAIN_SIZE = 5LL * GB_BYTES;

/* Check free space asynchronously to prevent hanging the UI thread.

   Up to one request to check a path is in flight to this thread; when the check()
   function runs, the current path is requested from the associated Intro object.
   The reply is sent back through a signal.

   This ensures that no queue of checking requests is built up while the user is
   still entering the path, and that always the most recently entered path is checked as
   soon as the thread becomes available.
*/
class FreespaceChecker : public QObject
{
    Q_OBJECT

public:
    FreespaceChecker(Intro *intro);

    enum Status {
        ST_OK,
        ST_ERROR
    };

public slots:
    void check();

signals:
    void reply(int status, const QString &message, quint64 available);

private:
    Intro *intro;
};

#include "intro.moc"

FreespaceChecker::FreespaceChecker(Intro *intro)
{
    this->intro = intro;
}

void FreespaceChecker::check()
{
    namespace fs = boost::filesystem;
    QString dataDirStr = intro->getPathToCheck();
    fs::path dataDir = fs::path(dataDirStr.toStdString());
    uint64_t freeBytesAvailable = 0;
    int replyStatus = ST_OK;
    QString replyMessage = tr("A new data directory will be created.");

    /* Find first parent that exists, so that fs::space does not fail */
    fs::path parentDir = dataDir;
    fs::path parentDirOld = fs::path();
    while(parentDir.has_parent_path() && !fs::exists(parentDir))
    {
        parentDir = parentDir.parent_path();

        /* Check if we make any progress, break if not to prevent an infinite loop here */
        if (parentDirOld == parentDir)
            break;

        parentDirOld = parentDir;
    }

    try {
        freeBytesAvailable = fs::space(parentDir).available;
        if(fs::exists(dataDir))
        {
            if(fs::is_directory(dataDir))
            {
                QString separator = "" + QDir::toNativeSeparators("/") + tr("name") + "";
                replyStatus = ST_OK;
                replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
            } else {
                replyStatus = ST_ERROR;
                replyMessage = tr("Path already exists, and is not a directory.");
            }
        }
    } catch(fs::filesystem_error &e)
    {
        /* Parent directory does not exist or is not accessible */
        replyStatus = ST_ERROR;
        replyMessage = tr("Cannot create data directory here.");
    }
    emit reply(replyStatus, replyMessage, freeBytesAvailable);
}

Intro::Intro(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Intro),
    thread(0),
    signalled(false)
{
    ui->setupUi(this);
    ui->sizeWarningLabel->setText(ui->sizeWarningLabel->text().arg(BLOCK_CHAIN_SIZE/GB_BYTES));
    startThread();
}

Intro::~Intro()
{
    delete ui;
    /* Ensure thread is finished before it is deleted */
    emit stopThread();
    thread->wait();
}

QString Intro::getDataDirectory()
{
    return ui->dataDirectory->text();
}

void Intro::setDataDirectory(const QString &dataDir)
{
    ui->dataDirectory->setText(dataDir);
    if(dataDir == getDefaultDataDirectory())
    {
        ui->dataDirDefault->setChecked(true);
        ui->dataDirectory->setEnabled(false);
        ui->ellipsisButton->setEnabled(false);
    } else {
        ui->dataDirCustom->setChecked(true);
        ui->dataDirectory->setEnabled(true);
        ui->ellipsisButton->setEnabled(true);
    }
}

QString Intro::getDefaultDataDirectory()
{
    return QString::fromStdString(GetDefaultDataDir().string());
}

void Intro::pickDataDirectory()
{
    namespace fs = boost::filesystem;
    QSettings settings;
    /* If data directory provided on command line, no need to look at settings
       or show a picking dialog */
    if(!GetArg("-datadir", "").empty())
        return;
    /* 1) Default data directory for operating system */
    QString dataDir = getDefaultDataDirectory();
    /* 2) Allow QSettings to override default dir */
    dataDir = settings.value("strDataDir", dataDir).toString();

    if(!fs::exists(dataDir.toStdString()) || GetBoolArg("-choosedatadir", false))
    {
        /* If current default data directory does not exist, let the user choose one */
        Intro intro;
        intro.setDataDirectory(dataDir);
        intro.setWindowIcon(QIcon(":icons/bitcoin"));

        while(true)
        {
            if(!intro.exec())
            {
                /* Cancel clicked */
                exit(0);
            }
            dataDir = intro.getDataDirectory();
            try {
                fs::create_directory(dataDir.toStdString());
                break;
            } catch(fs::filesystem_error &e) {
                QMessageBox::critical(0, tr("Bitcoin"),
                    tr("Error: Specified data directory \"%1\" can not be created.").arg(dataDir));
                /* fall through, back to choosing screen */
            }
        }

        settings.setValue("strDataDir", dataDir);
    }
    SoftSetArg("-datadir", dataDir.toStdString());
}

void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
{
    switch(status)
    {
    case FreespaceChecker::ST_OK:
        ui->errorMessage->setText(message);
        ui->errorMessage->setStyleSheet("");
        break;
    case FreespaceChecker::ST_ERROR:
        ui->errorMessage->setText(tr("Error") + ": " + message);
        ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
        break;
    }
    /* Indicate number of bytes available */
    if(status == FreespaceChecker::ST_ERROR)
    {
        ui->freeSpace->setText("");
    } else {
        QString freeString = QString::number(bytesAvailable/GB_BYTES) + tr("GB of free space available");
        if(bytesAvailable < BLOCK_CHAIN_SIZE)
        {
            freeString += " " + tr("(of %1GB needed)").arg(BLOCK_CHAIN_SIZE/GB_BYTES);
            ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
        } else {
            ui->freeSpace->setStyleSheet("");
        }
        ui->freeSpace->setText(freeString + ".");
    }
    /* Don't allow confirm in ERROR state */
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
}

void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
{
    /* Disable OK button until check result comes in */
    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
    checkPath(dataDirStr);
}

void Intro::on_ellipsisButton_clicked()
{
    QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(0, "Choose data directory", ui->dataDirectory->text()));
    if(!dir.isEmpty())
        ui->dataDirectory->setText(dir);
}

void Intro::on_dataDirDefault_clicked()
{
    setDataDirectory(getDefaultDataDirectory());
}

void Intro::on_dataDirCustom_clicked()
{
    ui->dataDirectory->setEnabled(true);
    ui->ellipsisButton->setEnabled(true);
}

void Intro::startThread()
{
    thread = new QThread(this);
    FreespaceChecker *executor = new FreespaceChecker(this);
    executor->moveToThread(thread);

    connect(executor, SIGNAL(reply(int,QString,quint64)), this, SLOT(setStatus(int,QString,quint64)));
    connect(this, SIGNAL(requestCheck()), executor, SLOT(check()));
    /*  make sure executor object is deleted in its own thread */
    connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
    connect(this, SIGNAL(stopThread()), thread, SLOT(quit()));

    thread->start();
}

void Intro::checkPath(const QString &dataDir)
{
    mutex.lock();
    pathToCheck = dataDir;
    if(!signalled)
    {
        signalled = true;
        emit requestCheck();
    }
    mutex.unlock();
}

QString Intro::getPathToCheck()
{
    QString retval;
    mutex.lock();
    retval = pathToCheck;
    signalled = false; /* new request can be queued now */
    mutex.unlock();
    return retval;
}
<|endoftext|>


<commit_before>/*
 * Car.cpp
 *
 *  Created on: 2009-09-08
 *      Author: chudy
 */

#include "race/Car.h"
#include "race/Level.h"

#include <ClanLib/core.h>
#include <ClanLib/display.h>

Car::Car(float p_x, float p_y, float p_rotation) :
	m_level(NULL),
	m_sprite(),
	m_position(p_x, p_y),
	m_rotation(0, cl_degrees),
	m_turn(0.0f),
	m_acceleration(false),
	m_brake(false),
	m_speed(0.0f)
{

}

Car::~Car() {
}

void Car::draw(CL_GraphicContext &p_gc) {

	if (m_sprite.is_null()) {
		m_sprite = CL_Sprite(p_gc, "race/car", Stage::getResourceManager());
	}

	p_gc.push_modelview();

	p_gc.mult_translate(m_position.x, m_position.y);
	p_gc.mult_rotate(m_rotation, 0, 0, 1);

	m_sprite.draw(p_gc, 0, 0);

	p_gc.pop_modelview();
}

void Car::update(unsigned int elapsedTime) {

	static const float MAX_TURN_SPEED = 3.0f;
	static const float MIN_TURN_SPEED = 0.5f;

	static const float BRAKE_POWER = 400.0f;

	static const float ACCEL_SPEED = 300.0f;
	static const float MAX_SPEED = 500.0f;

	static const float AIR_RESIST = 0.2f;

	static const float TURN_RATIO = ( MAX_TURN_SPEED - MIN_TURN_SPEED ) / ( ( 1.0f - MAX_SPEED ) * ( 1.0f - MAX_SPEED ) );

	static const float MAX_ANGLE = 50.0f;

	const float delta = elapsedTime / 1000.0f;

	float turn_speed = 1.0f;

	// turning speed
	if( m_speed > 0.0f )
		turn_speed = TURN_RATIO * ( m_speed - MAX_SPEED ) * ( m_speed - MAX_SPEED ) + MIN_TURN_SPEED;
	else
		turn_speed = TURN_RATIO * ( -m_speed - MAX_SPEED ) * ( -m_speed - MAX_SPEED ) + MIN_TURN_SPEED;

	if (m_turn != 0.0f) {
		m_angle += m_turn * delta * turn_speed * 360.0f * 1.4f;
		last_turn = m_turn;
		if( m_angle > MAX_ANGLE ) {
			m_angle = MAX_ANGLE;
		} else if ( m_angle < -MAX_ANGLE ) {
			m_angle = -MAX_ANGLE;
		}
	} else {
		m_angle += -last_turn * delta * turn_speed * 360.0f * 4.0f;
		if( last_turn == 1.0f && m_angle < 0.0f )
			m_angle = 0.0f;
		else if( last_turn == -1.0f && m_angle > 0.0f )
			m_angle = 0.0f;
	}

	// turning

	const CL_Angle deltaAngle(m_angle * delta * turn_speed * ( m_speed / 100.0f ), cl_degrees);
	m_rotation += deltaAngle;

	// acceleration

	if (m_acceleration) {
		const float speedChange = ACCEL_SPEED * delta;

		m_speed += speedChange;

		if (m_speed > MAX_SPEED) {
			m_speed = MAX_SPEED;
		}

	}

	// brake
	if (m_brake) {
		const float speedChange = BRAKE_POWER * delta;

		m_speed -= speedChange;

		if (m_speed < -MAX_SPEED / 2) {
			m_speed = -MAX_SPEED / 2;
		}
	}

	// air resistance
	m_speed -= delta * AIR_RESIST * m_speed;

	// ground resistance
	if (m_level != NULL) {
		const float groundResist = m_level->getResistance(m_position.x, m_position.y);
		m_speed -= delta * groundResist * m_speed;
	}

	// rotation

	const float rad = m_rotation.to_radians();

	m_moveVector.x = cos(rad);
	m_moveVector.y = sin(rad);

	m_moveVector.normalize();
	m_moveVector *= m_speed;

	// movement

	CL_Vec2f currentMoveVector = m_moveVector * delta;
	m_position.x += currentMoveVector.x;
	m_position.y += currentMoveVector.y;

#ifndef NDEBUG
			Stage::getDebugLayer()->putMessage(CL_String8("speed"),  CL_StringHelp::float_to_local8(m_speed));
			if (m_level != NULL) {
				Stage::getDebugLayer()->putMessage(CL_String8("resist"), CL_StringHelp::float_to_local8(m_level->getResistance(m_position.x, m_position.y)));
			}
#endif
}

int Car::prepareStatusEvent(CL_NetGameEvent &p_event) {
	CL_NetGameEventValue posX(m_position.x);
	CL_NetGameEventValue posY(m_position.y);
	CL_NetGameEventValue rotation(m_rotation.to_degrees());
	CL_NetGameEventValue turn(m_turn);
	CL_NetGameEventValue accel(m_acceleration);
	CL_NetGameEventValue brake(m_brake);
	CL_NetGameEventValue moveX(m_moveVector.x);
	CL_NetGameEventValue moveY(m_moveVector.y);
	CL_NetGameEventValue speed(m_speed);

	int c = 0;

	p_event.add_argument(posX);     ++c;
	p_event.add_argument(posY);     ++c;
	p_event.add_argument(rotation); ++c;
	p_event.add_argument(turn);     ++c;
	p_event.add_argument(accel);    ++c;
	p_event.add_argument(brake);    ++c;
	p_event.add_argument(moveX);    ++c;
	p_event.add_argument(moveY);    ++c;
	p_event.add_argument(speed);    ++c;

	return c;
}

int Car::applyStatusEvent(const CL_NetGameEvent &p_event, int p_beginIndex) {
	int i = p_beginIndex;

	m_position.x =   (float) p_event.get_argument(i++);
	m_position.y =   (float) p_event.get_argument(i++);
	m_rotation =     CL_Angle((float) p_event.get_argument(i++), cl_degrees);
	m_turn =         (float) p_event.get_argument(i++);
	m_acceleration =  (bool) p_event.get_argument(i++);
	m_brake =         (bool) p_event.get_argument(i++);
	m_moveVector.x = (float) p_event.get_argument(i++);
	m_moveVector.y = (float) p_event.get_argument(i++);
	m_speed =        (float) p_event.get_argument(i++);

	return i;
}
<commit_msg>smooth<commit_after>/*
 * Car.cpp
 *
 *  Created on: 2009-09-08
 *      Author: chudy
 */

#include "race/Car.h"
#include "race/Level.h"

#include <ClanLib/core.h>
#include <ClanLib/display.h>

Car::Car(float p_x, float p_y, float p_rotation) :
	m_level(NULL),
	m_sprite(),
	m_position(p_x, p_y),
	m_rotation(0, cl_degrees),
	m_turn(0.0f),
	m_acceleration(false),
	m_brake(false),
	m_speed(0.0f)
{

}

Car::~Car() {
}

void Car::draw(CL_GraphicContext &p_gc) {

	if (m_sprite.is_null()) {
		m_sprite = CL_Sprite(p_gc, "race/car", Stage::getResourceManager());
	}

	p_gc.push_modelview();

	p_gc.mult_translate(m_position.x, m_position.y);
	p_gc.mult_rotate(m_rotation, 0, 0, 1);

	m_sprite.draw(p_gc, 0, 0);

	p_gc.pop_modelview();
}

void Car::update(unsigned int elapsedTime) {

	static const float MAX_TURN_SPEED = 5.0f;
	static const float MIN_TURN_SPEED = 0.5f;

	static const float BRAKE_POWER = 400.0f;

	static const float ACCEL_SPEED = 300.0f;
	static const float MAX_SPEED = 500.0f;

	static const float AIR_RESIST = 0.2f;

	static const float TURN_RATIO = ( MAX_TURN_SPEED - MIN_TURN_SPEED ) / ( ( 1.0f - MAX_SPEED ) * ( 1.0f - MAX_SPEED ) );

	static const float MAX_ANGLE = 60.0f;

	const float delta = elapsedTime / 1000.0f;

	float turn_speed = 1.0f;

	// turning speed
	if( m_speed > 0.0f )
		turn_speed = TURN_RATIO * ( m_speed - MAX_SPEED ) * ( m_speed - MAX_SPEED ) + MIN_TURN_SPEED;
	else
		turn_speed = TURN_RATIO * ( -m_speed - MAX_SPEED ) * ( -m_speed - MAX_SPEED ) + MIN_TURN_SPEED;

	if (m_turn != 0.0f) {
		m_angle += m_turn * delta * turn_speed * 360.0f * 1.8f;
		last_turn = m_turn;
		if( m_angle > MAX_ANGLE ) {
			m_angle = MAX_ANGLE;
		} else if ( m_angle < -MAX_ANGLE ) {
			m_angle = -MAX_ANGLE;
		}
	} else {
		m_angle += -last_turn * delta * turn_speed * 360.0f * 5.0f;
		if( last_turn == 1.0f && m_angle < 0.0f )
			m_angle = 0.0f;
		else if( last_turn == -1.0f && m_angle > 0.0f )
			m_angle = 0.0f;
	}

	// turning

	const CL_Angle deltaAngle(m_angle * delta * turn_speed * ( m_speed / 100.0f ), cl_degrees);
	m_rotation += deltaAngle;

	// acceleration

	if (m_acceleration) {
		const float speedChange = ACCEL_SPEED * delta;

		m_speed += speedChange;

		if (m_speed > MAX_SPEED) {
			m_speed = MAX_SPEED;
		}

	}

	// brake
	if (m_brake) {
		const float speedChange = BRAKE_POWER * delta;

		m_speed -= speedChange;

		if (m_speed < -MAX_SPEED / 2) {
			m_speed = -MAX_SPEED / 2;
		}
	}

	// air resistance
	m_speed -= delta * AIR_RESIST * m_speed;

	// ground resistance
	if (m_level != NULL) {
		const float groundResist = m_level->getResistance(m_position.x, m_position.y);
		m_speed -= delta * groundResist * m_speed;
	}

	// rotation

	const float rad = m_rotation.to_radians();

	m_moveVector.x = cos(rad);
	m_moveVector.y = sin(rad);

	m_moveVector.normalize();
	m_moveVector *= m_speed;

	// movement

	CL_Vec2f currentMoveVector = m_moveVector * delta;
	m_position.x += currentMoveVector.x;
	m_position.y += currentMoveVector.y;

#ifndef NDEBUG
			Stage::getDebugLayer()->putMessage(CL_String8("speed"),  CL_StringHelp::float_to_local8(m_speed));
			if (m_level != NULL) {
				Stage::getDebugLayer()->putMessage(CL_String8("resist"), CL_StringHelp::float_to_local8(m_level->getResistance(m_position.x, m_position.y)));
			}
#endif
}

int Car::prepareStatusEvent(CL_NetGameEvent &p_event) {
	CL_NetGameEventValue posX(m_position.x);
	CL_NetGameEventValue posY(m_position.y);
	CL_NetGameEventValue rotation(m_rotation.to_degrees());
	CL_NetGameEventValue turn(m_turn);
	CL_NetGameEventValue accel(m_acceleration);
	CL_NetGameEventValue brake(m_brake);
	CL_NetGameEventValue moveX(m_moveVector.x);
	CL_NetGameEventValue moveY(m_moveVector.y);
	CL_NetGameEventValue speed(m_speed);

	int c = 0;

	p_event.add_argument(posX);     ++c;
	p_event.add_argument(posY);     ++c;
	p_event.add_argument(rotation); ++c;
	p_event.add_argument(turn);     ++c;
	p_event.add_argument(accel);    ++c;
	p_event.add_argument(brake);    ++c;
	p_event.add_argument(moveX);    ++c;
	p_event.add_argument(moveY);    ++c;
	p_event.add_argument(speed);    ++c;

	return c;
}

int Car::applyStatusEvent(const CL_NetGameEvent &p_event, int p_beginIndex) {
	int i = p_beginIndex;

	m_position.x =   (float) p_event.get_argument(i++);
	m_position.y =   (float) p_event.get_argument(i++);
	m_rotation =     CL_Angle((float) p_event.get_argument(i++), cl_degrees);
	m_turn =         (float) p_event.get_argument(i++);
	m_acceleration =  (bool) p_event.get_argument(i++);
	m_brake =         (bool) p_event.get_argument(i++);
	m_moveVector.x = (float) p_event.get_argument(i++);
	m_moveVector.y = (float) p_event.get_argument(i++);
	m_speed =        (float) p_event.get_argument(i++);

	return i;
}
<|endoftext|>


<commit_before>/*
* PKCS #1 v1.5 signature padding
* (C) 1999-2008 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/emsa_pkcs1.h>
#include <botan/hash_id.h>
#include <botan/exceptn.h>
#include <botan/oids.h>
#include <botan/pk_keys.h>
#include <botan/internal/padding.h>

namespace Botan {

namespace {

secure_vector<uint8_t> emsa3_encoding(const secure_vector<uint8_t>& msg,
                                   size_t output_bits,
                                   const uint8_t hash_id[],
                                   size_t hash_id_length)
   {
   size_t output_length = output_bits / 8;
   if(output_length < hash_id_length + msg.size() + 10)
      throw Encoding_Error("emsa3_encoding: Output length is too small");

   secure_vector<uint8_t> T(output_length);
   const size_t P_LENGTH = output_length - msg.size() - hash_id_length - 2;

   T[0] = 0x01;
   set_mem(&T[1], P_LENGTH, 0xFF);
   T[P_LENGTH+1] = 0x00;

   if(hash_id_length > 0)
      {
      BOTAN_ASSERT_NONNULL(hash_id);
      buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length);
      }

   buffer_insert(T, output_length-msg.size(), msg.data(), msg.size());
   return T;
   }

}

void EMSA_PKCS1v15::update(const uint8_t input[], size_t length)
   {
   m_hash->update(input, length);
   }

secure_vector<uint8_t> EMSA_PKCS1v15::raw_data()
   {
   return m_hash->final();
   }

secure_vector<uint8_t>
EMSA_PKCS1v15::encoding_of(const secure_vector<uint8_t>& msg,
                           size_t output_bits,
                           RandomNumberGenerator&)
   {
   if(msg.size() != m_hash->output_length())
      throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length");

   return emsa3_encoding(msg, output_bits,
                         m_hash_id.data(), m_hash_id.size());
   }

bool EMSA_PKCS1v15::verify(const secure_vector<uint8_t>& coded,
                           const secure_vector<uint8_t>& raw,
                           size_t key_bits)
   {
   if(raw.size() != m_hash->output_length())
      return false;

   try
      {
      return (coded == emsa3_encoding(raw, key_bits,
                                      m_hash_id.data(), m_hash_id.size()));
      }
   catch(...)
      {
      return false;
      }
   }

AlgorithmIdentifier EMSA_PKCS1v15::config_for_x509(const Private_Key& key,
                                    const std::string& cert_hash_name) const
   {
   if(cert_hash_name != m_hash->name())
      throw Invalid_Argument("Hash function from opts and hash_fn argument"
         " need to be identical");
   // check that the signature algorithm and the padding scheme fit
   if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA3"))
      {
      throw Invalid_Argument("Encoding scheme with canonical name EMSA3"
         " not supported for signature algorithm " + key.algo_name());
      }

   AlgorithmIdentifier sig_algo;
   sig_algo.oid = OIDS::lookup( key.algo_name() + "/" + name() );
   // for RSA PKCSv1.5 parameters "SHALL" be NULL as configured by
   // RSA_PublicKey::algorithm_identifier()
   sig_algo.parameters = key.algorithm_identifier().parameters;
   return sig_algo;
   }

EMSA_PKCS1v15::EMSA_PKCS1v15(HashFunction* hash) : m_hash(hash)
   {
   m_hash_id = pkcs_hash_id(m_hash->name());
   }

EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::string& hash_algo)
   {
   if(!hash_algo.empty())
      {
      m_hash_id = pkcs_hash_id(hash_algo);
      std::unique_ptr<HashFunction> hash(HashFunction::create(hash_algo));
      m_hash_name = hash->name();
      m_hash_output_len = hash->output_length();
      }
   else
      {
      m_hash_output_len = 0;
      }
   }

void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length)
   {
   m_message += std::make_pair(input, length);
   }

secure_vector<uint8_t> EMSA_PKCS1v15_Raw::raw_data()
   {
   secure_vector<uint8_t> ret;
   std::swap(ret, m_message);

   if(m_hash_output_len > 0 && ret.size() != m_hash_output_len)
      throw Encoding_Error("EMSA_PKCS1v15_Raw::encoding_of: Bad input length");

   return ret;
   }

secure_vector<uint8_t>
EMSA_PKCS1v15_Raw::encoding_of(const secure_vector<uint8_t>& msg,
                               size_t output_bits,
                               RandomNumberGenerator&)
   {
   return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size());
   }

bool EMSA_PKCS1v15_Raw::verify(const secure_vector<uint8_t>& coded,
                               const secure_vector<uint8_t>& raw,
                               size_t key_bits)
   {
   if(m_hash_output_len > 0 && raw.size() != m_hash_output_len)
      return false;

   try
      {
      return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size()));
      }
   catch(...)
      {
      return false;
      }
   }

}
<commit_msg>Fix crash in EMSA_PKCS1v15_Raw if the hash function was not enabled.<commit_after>/*
* PKCS #1 v1.5 signature padding
* (C) 1999-2008 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/emsa_pkcs1.h>
#include <botan/hash_id.h>
#include <botan/exceptn.h>
#include <botan/oids.h>
#include <botan/pk_keys.h>
#include <botan/internal/padding.h>

namespace Botan {

namespace {

secure_vector<uint8_t> emsa3_encoding(const secure_vector<uint8_t>& msg,
                                   size_t output_bits,
                                   const uint8_t hash_id[],
                                   size_t hash_id_length)
   {
   size_t output_length = output_bits / 8;
   if(output_length < hash_id_length + msg.size() + 10)
      throw Encoding_Error("emsa3_encoding: Output length is too small");

   secure_vector<uint8_t> T(output_length);
   const size_t P_LENGTH = output_length - msg.size() - hash_id_length - 2;

   T[0] = 0x01;
   set_mem(&T[1], P_LENGTH, 0xFF);
   T[P_LENGTH+1] = 0x00;

   if(hash_id_length > 0)
      {
      BOTAN_ASSERT_NONNULL(hash_id);
      buffer_insert(T, P_LENGTH+2, hash_id, hash_id_length);
      }

   buffer_insert(T, output_length-msg.size(), msg.data(), msg.size());
   return T;
   }

}

void EMSA_PKCS1v15::update(const uint8_t input[], size_t length)
   {
   m_hash->update(input, length);
   }

secure_vector<uint8_t> EMSA_PKCS1v15::raw_data()
   {
   return m_hash->final();
   }

secure_vector<uint8_t>
EMSA_PKCS1v15::encoding_of(const secure_vector<uint8_t>& msg,
                           size_t output_bits,
                           RandomNumberGenerator&)
   {
   if(msg.size() != m_hash->output_length())
      throw Encoding_Error("EMSA_PKCS1v15::encoding_of: Bad input length");

   return emsa3_encoding(msg, output_bits,
                         m_hash_id.data(), m_hash_id.size());
   }

bool EMSA_PKCS1v15::verify(const secure_vector<uint8_t>& coded,
                           const secure_vector<uint8_t>& raw,
                           size_t key_bits)
   {
   if(raw.size() != m_hash->output_length())
      return false;

   try
      {
      return (coded == emsa3_encoding(raw, key_bits,
                                      m_hash_id.data(), m_hash_id.size()));
      }
   catch(...)
      {
      return false;
      }
   }

AlgorithmIdentifier EMSA_PKCS1v15::config_for_x509(const Private_Key& key,
                                    const std::string& cert_hash_name) const
   {
   if(cert_hash_name != m_hash->name())
      throw Invalid_Argument("Hash function from opts and hash_fn argument"
         " need to be identical");
   // check that the signature algorithm and the padding scheme fit
   if(!sig_algo_and_pad_ok(key.algo_name(), "EMSA3"))
      {
      throw Invalid_Argument("Encoding scheme with canonical name EMSA3"
         " not supported for signature algorithm " + key.algo_name());
      }

   AlgorithmIdentifier sig_algo;
   sig_algo.oid = OIDS::lookup( key.algo_name() + "/" + name() );
   // for RSA PKCSv1.5 parameters "SHALL" be NULL as configured by
   // RSA_PublicKey::algorithm_identifier()
   sig_algo.parameters = key.algorithm_identifier().parameters;
   return sig_algo;
   }

EMSA_PKCS1v15::EMSA_PKCS1v15(HashFunction* hash) : m_hash(hash)
   {
   m_hash_id = pkcs_hash_id(m_hash->name());
   }

EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::string& hash_algo)
   {
   if(!hash_algo.empty())
      {
      m_hash_id = pkcs_hash_id(hash_algo);
      std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo));
      m_hash_name = hash->name();
      m_hash_output_len = hash->output_length();
      }
   else
      {
      m_hash_output_len = 0;
      }
   }

void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length)
   {
   m_message += std::make_pair(input, length);
   }

secure_vector<uint8_t> EMSA_PKCS1v15_Raw::raw_data()
   {
   secure_vector<uint8_t> ret;
   std::swap(ret, m_message);

   if(m_hash_output_len > 0 && ret.size() != m_hash_output_len)
      throw Encoding_Error("EMSA_PKCS1v15_Raw::encoding_of: Bad input length");

   return ret;
   }

secure_vector<uint8_t>
EMSA_PKCS1v15_Raw::encoding_of(const secure_vector<uint8_t>& msg,
                               size_t output_bits,
                               RandomNumberGenerator&)
   {
   return emsa3_encoding(msg, output_bits, m_hash_id.data(), m_hash_id.size());
   }

bool EMSA_PKCS1v15_Raw::verify(const secure_vector<uint8_t>& coded,
                               const secure_vector<uint8_t>& raw,
                               size_t key_bits)
   {
   if(m_hash_output_len > 0 && raw.size() != m_hash_output_len)
      return false;

   try
      {
      return (coded == emsa3_encoding(raw, key_bits, m_hash_id.data(), m_hash_id.size()));
      }
   catch(...)
      {
      return false;
      }
   }

}
<|endoftext|>


<commit_before>/*
* TLS Extension Pre Shared Key
* (C) 2022 Jack Lloyd
*     2022 René Meusel, neXenio GmbH
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/tls_cipher_state.h>
#include <botan/internal/tls_reader.h>
#include <botan/internal/stl_util.h>
#include <botan/tls_callbacks.h>
#include <botan/tls_exceptn.h>
#include <botan/tls_extensions.h>
#include <botan/tls_session.h>

#if defined(BOTAN_HAS_TLS_13)

namespace Botan::TLS {

namespace {

struct Client_PSK
   {
   std::vector<uint8_t> identity;
   std::vector<uint8_t> binder;
   uint32_t             obfuscated_ticket_age;

   std::string hash_algorithm;
   std::unique_ptr<Cipher_State> cipher_state;
   };

struct Server_PSK
   {
   uint16_t selected_identity;
   };

// RFC 8446 4.2.11.1
//    The "obfuscated_ticket_age" field of each PskIdentity contains an
//    obfuscated version of the ticket age formed by taking the age in
//    milliseconds and adding the "ticket_age_add" value that was included with
//    the ticket, modulo 2^32.
uint32_t obfuscate_ticket_age(std::chrono::milliseconds ticket_age, uint32_t ticket_age_add)
   {
   const uint64_t age = ticket_age.count();
   const uint64_t add = ticket_age_add;
   return static_cast<uint32_t>(age + add);
   }

}  // namespace

class PSK::PSK_Internal
   {
   public:
      PSK_Internal(Server_PSK srv_psk) : psk(std::move(srv_psk)) {}
      PSK_Internal(std::vector<Client_PSK> clt_psks) : psk(std::move(clt_psks)) {}

      std::variant<std::vector<Client_PSK>, Server_PSK> psk;
   };

PSK::PSK(TLS_Data_Reader& reader,
         uint16_t extension_size,
         Handshake_Type message_type)
   {
   if(message_type == SERVER_HELLO)
      {
      if(extension_size != 2)
         throw TLS_Exception(Alert::DECODE_ERROR, "Server provided a malformed PSK extension");

      m_impl = std::make_unique<PSK_Internal>(Server_PSK{reader.get_uint16_t()});
      }
   else if(message_type == CLIENT_HELLO)
      {
      std::vector<Client_PSK> psks;

      const auto identities_length = reader.get_uint16_t();
      const auto identities_offset = reader.read_so_far();

      while(reader.has_remaining() && (reader.read_so_far() - identities_offset) < identities_length)
         {
         auto& psk = psks.emplace_back();
         psk.identity = reader.get_tls_length_value(2);
         psk.obfuscated_ticket_age = reader.get_uint32_t();
         }

      if(reader.read_so_far() - identities_offset != identities_length)
         {
         throw TLS_Exception(Alert::DECODE_ERROR, "Inconsistent PSK identity list");
         }

      const auto binders_length = reader.get_uint16_t();
      const auto binders_offset = reader.read_so_far();

      for(auto& psk : psks)
         {
         if(!reader.has_remaining() || reader.read_so_far() - binders_offset >= binders_length)
            {
            throw TLS_Exception(Alert::DECODE_ERROR, "Not enough PSK binders");
            }

         psk.binder = reader.get_tls_length_value(1);
         }

      if(reader.read_so_far() - binders_offset != binders_length)
         {
         throw TLS_Exception(Alert::DECODE_ERROR, "Inconsistent PSK binders list");
         }

      m_impl = std::make_unique<PSK_Internal>(std::move(psks));
      }
   else
      {
      throw TLS_Exception(Alert::DECODE_ERROR, "Found a PSK extension in an unexpected handshake message");
      }
   }

PSK::PSK(const Session& session_to_resume, Callbacks& callbacks)
   {
   std::vector<Client_PSK> psks;
   auto& cpsk = psks.emplace_back();

   cpsk.identity = session_to_resume.session_ticket();

   const auto age =
      std::chrono::duration_cast<std::chrono::milliseconds>(
         callbacks.tls_current_timestamp() - session_to_resume.start_time());

   cpsk.obfuscated_ticket_age =
      obfuscate_ticket_age(age, session_to_resume.session_age_add());

   auto psk = session_to_resume.master_secret();
   cpsk.hash_algorithm = session_to_resume.ciphersuite().prf_algo();
   cpsk.cipher_state = Cipher_State::init_with_psk(CLIENT,
                                                  Cipher_State::PSK_Type::RESUMPTION,
                                                  std::move(psk),
                                                  session_to_resume.ciphersuite());

   // RFC 8446 4.2.11.2
   //    Each entry in the binders list is computed as an HMAC over a transcript
   //    hash (see Section 4.4.1) containing a partial ClientHello up to and
   //    including the PreSharedKeyExtension.identities field.  That is, it
   //    includes all of the ClientHello but not the binders list itself.  The
   //    length fields for the message (including the overall length, the length
   //    of the extensions block, and the length of the "pre_shared_key"
   //    extension) are all set as if binders of the correct lengths were
   //    present.
   //
   // Hence, we fill the binders with dummy values of the correct length and use
   // `Client_Hello_13::truncate()` to split them off before calculating the
   // transcript hash that underpins the PSK binders. S.a. `calculate_binders()`
   const auto binder_length = HashFunction::create_or_throw(cpsk.hash_algorithm)->output_length();
   cpsk.binder = std::vector<uint8_t>(binder_length);

   m_impl = std::make_unique<PSK_Internal>(std::move(psks));
   }

PSK::~PSK() = default;

bool PSK::empty() const
   {
   if(std::holds_alternative<Server_PSK>(m_impl->psk))
      return false;

   BOTAN_ASSERT_NOMSG(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
   return std::get<std::vector<Client_PSK>>(m_impl->psk).empty();
   }

std::unique_ptr<Cipher_State> PSK::select_cipher_state(const PSK& server_psk, const Ciphersuite& cipher)
   {
   BOTAN_STATE_CHECK(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
   BOTAN_STATE_CHECK(std::holds_alternative<Server_PSK>(server_psk.m_impl->psk));

   const auto id = std::get<Server_PSK>(server_psk.m_impl->psk).selected_identity;
   auto& ids = std::get<std::vector<Client_PSK>>(m_impl->psk);

   // RFC 8446 4.2.11
   //    Clients MUST verify that the server's selected_identity is within the
   //    range supplied by the client, [...].  If these values are not
   //    consistent, the client MUST abort the handshake with an
   //    "illegal_parameter" alert.
   if(id >= ids.size())
      {
      throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "PSK identity selected by server is out of bounds");
      }

   auto cipher_state = std::exchange(ids[id].cipher_state, nullptr);
   BOTAN_ASSERT_NONNULL(cipher_state);

   // RFC 8446 4.2.11
   //    Clients MUST verify that [...] the server selected a cipher suite
   //    indicating a Hash associated with the PSK [...].  If these values
   //    are not consistent, the client MUST abort the handshake with an
   //   "illegal_parameter" alert.
   if(!cipher_state->is_compatible_with(cipher))
      {
      throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "PSK and ciphersuite selected by server are not compatible");
      }

   // destroy cipher states and PSKs that were not selected by the server
   ids.clear();

   return cipher_state;
   }

void PSK::filter(const Ciphersuite& cipher)
   {
   BOTAN_STATE_CHECK(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
   auto& psks = std::get<std::vector<Client_PSK>>(m_impl->psk);

   const auto r = std::remove_if(psks.begin(), psks.end(), [&](const auto& psk)
      { return psk.hash_algorithm != cipher.prf_algo(); });
   psks.erase(r, psks.end());
   }

std::vector<uint8_t> PSK::serialize(Connection_Side side) const
   {
   std::vector<uint8_t> result;

   std::visit(overloaded
      {
      [&](const Server_PSK& psk)
         {
         BOTAN_STATE_CHECK(side == SERVER);
         result.reserve(2);
         result.push_back(get_byte<0>(psk.selected_identity));
         result.push_back(get_byte<1>(psk.selected_identity));
         },
      [&](const std::vector<Client_PSK>& psks)
         {
         BOTAN_STATE_CHECK(side == CLIENT);

         std::vector<uint8_t> identities;
         std::vector<uint8_t> binders;
         for(const auto& psk : psks)
            {
            append_tls_length_value(identities, psk.identity, 2);
            identities.push_back(get_byte<0>(psk.obfuscated_ticket_age));
            identities.push_back(get_byte<1>(psk.obfuscated_ticket_age));
            identities.push_back(get_byte<2>(psk.obfuscated_ticket_age));
            identities.push_back(get_byte<3>(psk.obfuscated_ticket_age));

            append_tls_length_value(binders, psk.binder, 1);
            }

         append_tls_length_value(result, identities, 2);
         append_tls_length_value(result, binders, 2);
         },
      },
      m_impl->psk);

   return result;
   }

// See RFC 8446 4.2.11.2 for details on how these binders are calculated
void PSK::calculate_binders(const Transcript_Hash_State& truncated_transcript_hash)
   {
   BOTAN_ASSERT_NOMSG(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
   for(auto& psk : std::get<std::vector<Client_PSK>>(m_impl->psk))
      {
      auto tth = truncated_transcript_hash.clone();
      tth.set_algorithm(psk.hash_algorithm);
      BOTAN_ASSERT_NONNULL(psk.cipher_state);
      psk.binder = psk.cipher_state->psk_binder_mac(tth.truncated());
      }
   }

}  // Botan::TLS

#endif  // HAS_TLS_13
<commit_msg>FIX: missing include<commit_after>/*
* TLS Extension Pre Shared Key
* (C) 2022 Jack Lloyd
*     2022 René Meusel, neXenio GmbH
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/internal/tls_cipher_state.h>
#include <botan/internal/tls_reader.h>
#include <botan/internal/stl_util.h>
#include <botan/tls_callbacks.h>
#include <botan/tls_exceptn.h>
#include <botan/tls_extensions.h>
#include <botan/tls_session.h>

#include <utility>

#if defined(BOTAN_HAS_TLS_13)

namespace Botan::TLS {

namespace {

struct Client_PSK
   {
   std::vector<uint8_t> identity;
   std::vector<uint8_t> binder;
   uint32_t             obfuscated_ticket_age;

   std::string hash_algorithm;
   std::unique_ptr<Cipher_State> cipher_state;
   };

struct Server_PSK
   {
   uint16_t selected_identity;
   };

// RFC 8446 4.2.11.1
//    The "obfuscated_ticket_age" field of each PskIdentity contains an
//    obfuscated version of the ticket age formed by taking the age in
//    milliseconds and adding the "ticket_age_add" value that was included with
//    the ticket, modulo 2^32.
uint32_t obfuscate_ticket_age(std::chrono::milliseconds ticket_age, uint32_t ticket_age_add)
   {
   const uint64_t age = ticket_age.count();
   const uint64_t add = ticket_age_add;
   return static_cast<uint32_t>(age + add);
   }

}  // namespace

class PSK::PSK_Internal
   {
   public:
      PSK_Internal(Server_PSK srv_psk) : psk(std::move(srv_psk)) {}
      PSK_Internal(std::vector<Client_PSK> clt_psks) : psk(std::move(clt_psks)) {}

      std::variant<std::vector<Client_PSK>, Server_PSK> psk;
   };

PSK::PSK(TLS_Data_Reader& reader,
         uint16_t extension_size,
         Handshake_Type message_type)
   {
   if(message_type == SERVER_HELLO)
      {
      if(extension_size != 2)
         throw TLS_Exception(Alert::DECODE_ERROR, "Server provided a malformed PSK extension");

      m_impl = std::make_unique<PSK_Internal>(Server_PSK{reader.get_uint16_t()});
      }
   else if(message_type == CLIENT_HELLO)
      {
      std::vector<Client_PSK> psks;

      const auto identities_length = reader.get_uint16_t();
      const auto identities_offset = reader.read_so_far();

      while(reader.has_remaining() && (reader.read_so_far() - identities_offset) < identities_length)
         {
         auto& psk = psks.emplace_back();
         psk.identity = reader.get_tls_length_value(2);
         psk.obfuscated_ticket_age = reader.get_uint32_t();
         }

      if(reader.read_so_far() - identities_offset != identities_length)
         {
         throw TLS_Exception(Alert::DECODE_ERROR, "Inconsistent PSK identity list");
         }

      const auto binders_length = reader.get_uint16_t();
      const auto binders_offset = reader.read_so_far();

      for(auto& psk : psks)
         {
         if(!reader.has_remaining() || reader.read_so_far() - binders_offset >= binders_length)
            {
            throw TLS_Exception(Alert::DECODE_ERROR, "Not enough PSK binders");
            }

         psk.binder = reader.get_tls_length_value(1);
         }

      if(reader.read_so_far() - binders_offset != binders_length)
         {
         throw TLS_Exception(Alert::DECODE_ERROR, "Inconsistent PSK binders list");
         }

      m_impl = std::make_unique<PSK_Internal>(std::move(psks));
      }
   else
      {
      throw TLS_Exception(Alert::DECODE_ERROR, "Found a PSK extension in an unexpected handshake message");
      }
   }

PSK::PSK(const Session& session_to_resume, Callbacks& callbacks)
   {
   std::vector<Client_PSK> psks;
   auto& cpsk = psks.emplace_back();

   cpsk.identity = session_to_resume.session_ticket();

   const auto age =
      std::chrono::duration_cast<std::chrono::milliseconds>(
         callbacks.tls_current_timestamp() - session_to_resume.start_time());

   cpsk.obfuscated_ticket_age =
      obfuscate_ticket_age(age, session_to_resume.session_age_add());

   auto psk = session_to_resume.master_secret();
   cpsk.hash_algorithm = session_to_resume.ciphersuite().prf_algo();
   cpsk.cipher_state = Cipher_State::init_with_psk(CLIENT,
                                                  Cipher_State::PSK_Type::RESUMPTION,
                                                  std::move(psk),
                                                  session_to_resume.ciphersuite());

   // RFC 8446 4.2.11.2
   //    Each entry in the binders list is computed as an HMAC over a transcript
   //    hash (see Section 4.4.1) containing a partial ClientHello up to and
   //    including the PreSharedKeyExtension.identities field.  That is, it
   //    includes all of the ClientHello but not the binders list itself.  The
   //    length fields for the message (including the overall length, the length
   //    of the extensions block, and the length of the "pre_shared_key"
   //    extension) are all set as if binders of the correct lengths were
   //    present.
   //
   // Hence, we fill the binders with dummy values of the correct length and use
   // `Client_Hello_13::truncate()` to split them off before calculating the
   // transcript hash that underpins the PSK binders. S.a. `calculate_binders()`
   const auto binder_length = HashFunction::create_or_throw(cpsk.hash_algorithm)->output_length();
   cpsk.binder = std::vector<uint8_t>(binder_length);

   m_impl = std::make_unique<PSK_Internal>(std::move(psks));
   }

PSK::~PSK() = default;

bool PSK::empty() const
   {
   if(std::holds_alternative<Server_PSK>(m_impl->psk))
      return false;

   BOTAN_ASSERT_NOMSG(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
   return std::get<std::vector<Client_PSK>>(m_impl->psk).empty();
   }

std::unique_ptr<Cipher_State> PSK::select_cipher_state(const PSK& server_psk, const Ciphersuite& cipher)
   {
   BOTAN_STATE_CHECK(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
   BOTAN_STATE_CHECK(std::holds_alternative<Server_PSK>(server_psk.m_impl->psk));

   const auto id = std::get<Server_PSK>(server_psk.m_impl->psk).selected_identity;
   auto& ids = std::get<std::vector<Client_PSK>>(m_impl->psk);

   // RFC 8446 4.2.11
   //    Clients MUST verify that the server's selected_identity is within the
   //    range supplied by the client, [...].  If these values are not
   //    consistent, the client MUST abort the handshake with an
   //    "illegal_parameter" alert.
   if(id >= ids.size())
      {
      throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "PSK identity selected by server is out of bounds");
      }

   auto cipher_state = std::exchange(ids[id].cipher_state, nullptr);
   BOTAN_ASSERT_NONNULL(cipher_state);

   // RFC 8446 4.2.11
   //    Clients MUST verify that [...] the server selected a cipher suite
   //    indicating a Hash associated with the PSK [...].  If these values
   //    are not consistent, the client MUST abort the handshake with an
   //   "illegal_parameter" alert.
   if(!cipher_state->is_compatible_with(cipher))
      {
      throw TLS_Exception(Alert::ILLEGAL_PARAMETER, "PSK and ciphersuite selected by server are not compatible");
      }

   // destroy cipher states and PSKs that were not selected by the server
   ids.clear();

   return cipher_state;
   }

void PSK::filter(const Ciphersuite& cipher)
   {
   BOTAN_STATE_CHECK(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
   auto& psks = std::get<std::vector<Client_PSK>>(m_impl->psk);

   const auto r = std::remove_if(psks.begin(), psks.end(), [&](const auto& psk)
      { return psk.hash_algorithm != cipher.prf_algo(); });
   psks.erase(r, psks.end());
   }

std::vector<uint8_t> PSK::serialize(Connection_Side side) const
   {
   std::vector<uint8_t> result;

   std::visit(overloaded
      {
      [&](const Server_PSK& psk)
         {
         BOTAN_STATE_CHECK(side == SERVER);
         result.reserve(2);
         result.push_back(get_byte<0>(psk.selected_identity));
         result.push_back(get_byte<1>(psk.selected_identity));
         },
      [&](const std::vector<Client_PSK>& psks)
         {
         BOTAN_STATE_CHECK(side == CLIENT);

         std::vector<uint8_t> identities;
         std::vector<uint8_t> binders;
         for(const auto& psk : psks)
            {
            append_tls_length_value(identities, psk.identity, 2);
            identities.push_back(get_byte<0>(psk.obfuscated_ticket_age));
            identities.push_back(get_byte<1>(psk.obfuscated_ticket_age));
            identities.push_back(get_byte<2>(psk.obfuscated_ticket_age));
            identities.push_back(get_byte<3>(psk.obfuscated_ticket_age));

            append_tls_length_value(binders, psk.binder, 1);
            }

         append_tls_length_value(result, identities, 2);
         append_tls_length_value(result, binders, 2);
         },
      },
      m_impl->psk);

   return result;
   }

// See RFC 8446 4.2.11.2 for details on how these binders are calculated
void PSK::calculate_binders(const Transcript_Hash_State& truncated_transcript_hash)
   {
   BOTAN_ASSERT_NOMSG(std::holds_alternative<std::vector<Client_PSK>>(m_impl->psk));
   for(auto& psk : std::get<std::vector<Client_PSK>>(m_impl->psk))
      {
      auto tth = truncated_transcript_hash.clone();
      tth.set_algorithm(psk.hash_algorithm);
      BOTAN_ASSERT_NONNULL(psk.cipher_state);
      psk.binder = psk.cipher_state->psk_binder_mac(tth.truncated());
      }
   }

}  // Botan::TLS

#endif  // HAS_TLS_13
<|endoftext|>