LCOV - code coverage report
Current view: top level - source/lib/allocators - dynarray.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 42 61 68.9 %
Date: 2023-01-19 00:18:29 Functions: 6 8 75.0 %

          Line data    Source code
       1             : /* Copyright (C) 2020 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : /*
      24             :  * dynamic (expandable) array
      25             :  */
      26             : 
      27             : #include "precompiled.h"
      28             : #include "lib/allocators/dynarray.h"
      29             : 
      30             : #include "lib/alignment.h"
      31             : #include "lib/sysdep/vm.h"
      32             : 
      33             : #include <cstring>
      34             : #include <string>
      35             : 
      36           4 : static Status validate_da(DynArray* da)
      37             : {
      38           4 :     if(!da)
      39           0 :         WARN_RETURN(ERR::INVALID_POINTER);
      40             : //  u8* const base           = da->base;
      41           4 :     const size_t max_size_pa = da->max_size_pa;
      42           4 :     const size_t cur_size    = da->cur_size;
      43           4 :     const size_t pos         = da->pos;
      44             : 
      45             :     // note: this happens if max_size == 0
      46             : //  if(debug_IsPointerBogus(base))
      47             : //      WARN_RETURN(ERR::_1);
      48             :     // note: don't check if base is page-aligned -
      49             :     // might not be true for 'wrapped' mem regions.
      50           4 :     if(!IsAligned(max_size_pa, g_PageSize))
      51           0 :         WARN_RETURN(ERR::_3);
      52           4 :     if(cur_size > max_size_pa)
      53           0 :         WARN_RETURN(ERR::_4);
      54           4 :     if(pos > cur_size || pos > max_size_pa)
      55           0 :         WARN_RETURN(ERR::_5);
      56             : 
      57           4 :     return INFO::OK;
      58             : }
      59             : 
      60             : #define CHECK_DA(da) RETURN_STATUS_IF_ERR(validate_da(da))
      61             : 
      62             : 
      63           1 : Status da_alloc(DynArray* da, size_t max_size)
      64             : {
      65           1 :     ENSURE(max_size != 0);
      66           1 :     const size_t max_size_pa = Align<g_PageSize>(max_size);
      67             : 
      68           1 :     u8* p = (u8*)vm::ReserveAddressSpace(max_size_pa);
      69           1 :     if(!p)
      70           0 :         return ERR::NO_MEM; // NOWARN (already done in vm)
      71             : 
      72           1 :     da->base        = p;
      73           1 :     da->max_size_pa = max_size_pa;
      74           1 :     da->cur_size    = 0;
      75           1 :     da->cur_size_pa = 0;
      76           1 :     da->pos         = 0;
      77           1 :     CHECK_DA(da);
      78           1 :     return INFO::OK;
      79             : }
      80             : 
      81             : 
      82           1 : Status da_free(DynArray* da)
      83             : {
      84           1 :     CHECK_DA(da);
      85             : 
      86           1 :     vm::ReleaseAddressSpace(da->base, da->max_size_pa);
      87             : 
      88             :     // wipe out the DynArray for safety
      89           1 :     memset(da, 0, sizeof(*da));
      90             : 
      91           1 :     return INFO::OK;
      92             : }
      93             : 
      94             : 
      95           1 : Status da_set_size(DynArray* da, size_t new_size)
      96             : {
      97           1 :     CHECK_DA(da);
      98             : 
      99             :     // determine how much to add/remove
     100           1 :     const size_t cur_size_pa = Align<g_PageSize>(da->cur_size);
     101           1 :     const size_t new_size_pa = Align<g_PageSize>(new_size);
     102           1 :     const ssize_t size_delta_pa = (ssize_t)new_size_pa - (ssize_t)cur_size_pa;
     103             : 
     104             :     // not enough memory to satisfy this expand request: abort.
     105             :     // note: do not complain - some allocators (e.g. file_cache)
     106             :     // legitimately use up all available space.
     107           1 :     if(new_size_pa > da->max_size_pa)
     108           0 :         return ERR::LIMIT;  // NOWARN
     109             : 
     110           1 :     u8* end = da->base + cur_size_pa;
     111           1 :     bool ok = true;
     112             :     // expanding
     113           1 :     if(size_delta_pa > 0)
     114             :     {
     115           1 :         ok = vm::Commit(uintptr_t(end), size_delta_pa);
     116           1 :         if(!ok)
     117           0 :             debug_printf("Commit failed (%p %lld)\n", (void *)end, (long long)size_delta_pa);
     118             :     }
     119             :     // shrinking
     120           0 :     else if(size_delta_pa < 0)
     121           0 :         ok = vm::Decommit(uintptr_t(end+size_delta_pa), -size_delta_pa);
     122             :     // else: no change in page count, e.g. if going from size=1 to 2
     123             :     // (we don't want mem_* to have to handle size=0)
     124             : 
     125           1 :     da->cur_size = new_size;
     126           1 :     da->cur_size_pa = new_size_pa;
     127           1 :     CHECK_DA(da);
     128           1 :     return ok? INFO::OK : ERR::FAIL;
     129             : }
     130             : 
     131             : 
     132           0 : Status da_reserve(DynArray* da, size_t size)
     133             : {
     134           0 :     if(da->pos+size > da->cur_size_pa)
     135           0 :         RETURN_STATUS_IF_ERR(da_set_size(da, da->cur_size_pa+size));
     136           0 :     da->cur_size = std::max(da->cur_size, da->pos+size);
     137           0 :     return INFO::OK;
     138             : }
     139             : 
     140             : 
     141           0 : Status da_append(DynArray* da, const void* data, size_t size)
     142             : {
     143           0 :     RETURN_STATUS_IF_ERR(da_reserve(da, size));
     144           0 :     memcpy(da->base+da->pos, data, size);
     145           0 :     da->pos += size;
     146           0 :     return INFO::OK;
     147           3 : }

Generated by: LCOV version 1.13