17ec681f3Smrg/**************************************************************************
27ec681f3Smrg *
37ec681f3Smrg * Copyright 2017 Valve Corporation
47ec681f3Smrg * All Rights Reserved.
57ec681f3Smrg *
67ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
77ec681f3Smrg * copy of this software and associated documentation files (the
87ec681f3Smrg * "Software"), to deal in the Software without restriction, including
97ec681f3Smrg * without limitation the rights to use, copy, modify, merge, publish,
107ec681f3Smrg * distribute, sub license, and/or sell copies of the Software, and to
117ec681f3Smrg * permit persons to whom the Software is furnished to do so, subject to
127ec681f3Smrg * the following conditions:
137ec681f3Smrg *
147ec681f3Smrg * The above copyright notice and this permission notice (including the
157ec681f3Smrg * next paragraph) shall be included in all copies or substantial portions
167ec681f3Smrg * of the Software.
177ec681f3Smrg *
187ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
197ec681f3Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
207ec681f3Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
217ec681f3Smrg * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
227ec681f3Smrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
237ec681f3Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
247ec681f3Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
257ec681f3Smrg *
267ec681f3Smrg **************************************************************************/
277ec681f3Smrg
287ec681f3Smrg/**
297ec681f3Smrg * @file
307ec681f3Smrg * A simple allocator that allocates and release "numbers".
317ec681f3Smrg *
327ec681f3Smrg * @author Samuel Pitoiset <samuel.pitoiset@gmail.com>
337ec681f3Smrg */
347ec681f3Smrg
357ec681f3Smrg#include "util/u_idalloc.h"
367ec681f3Smrg#include "util/u_math.h"
377ec681f3Smrg#include <stdlib.h>
387ec681f3Smrg
397ec681f3Smrgstatic void
407ec681f3Smrgutil_idalloc_resize(struct util_idalloc *buf, unsigned new_num_elements)
417ec681f3Smrg{
427ec681f3Smrg   if (new_num_elements > buf->num_elements) {
437ec681f3Smrg      buf->data = realloc(buf->data, new_num_elements * sizeof(*buf->data));
447ec681f3Smrg      memset(&buf->data[buf->num_elements], 0,
457ec681f3Smrg             (new_num_elements - buf->num_elements) * sizeof(*buf->data));
467ec681f3Smrg      buf->num_elements = new_num_elements;
477ec681f3Smrg   }
487ec681f3Smrg}
497ec681f3Smrg
507ec681f3Smrgvoid
517ec681f3Smrgutil_idalloc_init(struct util_idalloc *buf, unsigned initial_num_ids)
527ec681f3Smrg{
537ec681f3Smrg   memset(buf, 0, sizeof(*buf));
547ec681f3Smrg   assert(initial_num_ids);
557ec681f3Smrg   util_idalloc_resize(buf, DIV_ROUND_UP(initial_num_ids, 32));
567ec681f3Smrg}
577ec681f3Smrg
587ec681f3Smrgvoid
597ec681f3Smrgutil_idalloc_fini(struct util_idalloc *buf)
607ec681f3Smrg{
617ec681f3Smrg   if (buf->data)
627ec681f3Smrg      free(buf->data);
637ec681f3Smrg}
647ec681f3Smrg
657ec681f3Smrgunsigned
667ec681f3Smrgutil_idalloc_alloc(struct util_idalloc *buf)
677ec681f3Smrg{
687ec681f3Smrg   unsigned num_elements = buf->num_elements;
697ec681f3Smrg
707ec681f3Smrg   for (unsigned i = buf->lowest_free_idx; i < num_elements; i++) {
717ec681f3Smrg      if (buf->data[i] == 0xffffffff)
727ec681f3Smrg         continue;
737ec681f3Smrg
747ec681f3Smrg      unsigned bit = ffs(~buf->data[i]) - 1;
757ec681f3Smrg      buf->data[i] |= 1u << bit;
767ec681f3Smrg      buf->lowest_free_idx = i;
777ec681f3Smrg      return i * 32 + bit;
787ec681f3Smrg   }
797ec681f3Smrg
807ec681f3Smrg   /* No slots available, resize and return the first free. */
817ec681f3Smrg   util_idalloc_resize(buf, MAX2(num_elements, 1) * 2);
827ec681f3Smrg
837ec681f3Smrg   buf->lowest_free_idx = num_elements;
847ec681f3Smrg   buf->data[num_elements] |= 1;
857ec681f3Smrg   return num_elements * 32;
867ec681f3Smrg}
877ec681f3Smrg
887ec681f3Smrgstatic unsigned
897ec681f3Smrgfind_free_block(struct util_idalloc *buf, unsigned start)
907ec681f3Smrg{
917ec681f3Smrg   for (unsigned i = start; i < buf->num_elements; i++) {
927ec681f3Smrg      if (!buf->data[i])
937ec681f3Smrg         return i;
947ec681f3Smrg   }
957ec681f3Smrg   return buf->num_elements;
967ec681f3Smrg}
977ec681f3Smrg
987ec681f3Smrg/* Allocate a range of consecutive IDs. Return the first ID. */
997ec681f3Smrgunsigned
1007ec681f3Smrgutil_idalloc_alloc_range(struct util_idalloc *buf, unsigned num)
1017ec681f3Smrg{
1027ec681f3Smrg   if (num == 1)
1037ec681f3Smrg      return util_idalloc_alloc(buf);
1047ec681f3Smrg
1057ec681f3Smrg   unsigned num_alloc = DIV_ROUND_UP(num, 32);
1067ec681f3Smrg   unsigned num_elements = buf->num_elements;
1077ec681f3Smrg   unsigned base = find_free_block(buf, buf->lowest_free_idx);
1087ec681f3Smrg
1097ec681f3Smrg   while (1) {
1107ec681f3Smrg      unsigned i;
1117ec681f3Smrg      for (i = base;
1127ec681f3Smrg           i < num_elements && i - base < num_alloc && !buf->data[i]; i++);
1137ec681f3Smrg
1147ec681f3Smrg      if (i - base == num_alloc)
1157ec681f3Smrg         goto ret; /* found */
1167ec681f3Smrg
1177ec681f3Smrg      if (i == num_elements)
1187ec681f3Smrg         break; /* not found */
1197ec681f3Smrg
1207ec681f3Smrg      /* continue searching */
1217ec681f3Smrg      base = !buf->data[i] ? i : i + 1;
1227ec681f3Smrg   }
1237ec681f3Smrg
1247ec681f3Smrg   /* No slots available, allocate more. */
1257ec681f3Smrg   util_idalloc_resize(buf, num_elements * 2 + num_alloc);
1267ec681f3Smrg
1277ec681f3Smrgret:
1287ec681f3Smrg   /* Mark the bits as used. */
1297ec681f3Smrg   for (unsigned i = base; i < base + num_alloc - (num % 32 != 0); i++)
1307ec681f3Smrg      buf->data[i] = 0xffffffff;
1317ec681f3Smrg   if (num % 32 != 0)
1327ec681f3Smrg      buf->data[base + num_alloc - 1] |= BITFIELD_MASK(num % 32);
1337ec681f3Smrg
1347ec681f3Smrg   if (buf->lowest_free_idx == base)
1357ec681f3Smrg      buf->lowest_free_idx = base + num / 32;
1367ec681f3Smrg
1377ec681f3Smrg   /* Validate this algorithm. */
1387ec681f3Smrg   for (unsigned i = 0; i < num; i++)
1397ec681f3Smrg      assert(util_idalloc_exists(buf, base * 32 + i));
1407ec681f3Smrg
1417ec681f3Smrg   return base * 32;
1427ec681f3Smrg}
1437ec681f3Smrg
1447ec681f3Smrgvoid
1457ec681f3Smrgutil_idalloc_free(struct util_idalloc *buf, unsigned id)
1467ec681f3Smrg{
1477ec681f3Smrg   assert(id / 32 < buf->num_elements);
1487ec681f3Smrg   unsigned idx = id / 32;
1497ec681f3Smrg   buf->lowest_free_idx = MIN2(idx, buf->lowest_free_idx);
1507ec681f3Smrg   buf->data[idx] &= ~(1 << (id % 32));
1517ec681f3Smrg}
1527ec681f3Smrg
1537ec681f3Smrgvoid
1547ec681f3Smrgutil_idalloc_reserve(struct util_idalloc *buf, unsigned id)
1557ec681f3Smrg{
1567ec681f3Smrg   if (id / 32 >= buf->num_elements)
1577ec681f3Smrg      util_idalloc_resize(buf, (id / 32 + 1) * 2);
1587ec681f3Smrg   buf->data[id / 32] |= 1u << (id % 32);
1597ec681f3Smrg}
1607ec681f3Smrg
1617ec681f3Smrgvoid
1627ec681f3Smrgutil_idalloc_mt_init(struct util_idalloc_mt *buf,
1637ec681f3Smrg                     unsigned initial_num_ids, bool skip_zero)
1647ec681f3Smrg{
1657ec681f3Smrg   simple_mtx_init(&buf->mutex, mtx_plain);
1667ec681f3Smrg   util_idalloc_init(&buf->buf, initial_num_ids);
1677ec681f3Smrg   buf->skip_zero = skip_zero;
1687ec681f3Smrg
1697ec681f3Smrg   if (skip_zero) {
1707ec681f3Smrg      ASSERTED unsigned zero = util_idalloc_alloc(&buf->buf);
1717ec681f3Smrg      assert(zero == 0);
1727ec681f3Smrg   }
1737ec681f3Smrg}
1747ec681f3Smrg
1757ec681f3Smrg/* Callback for drivers using u_threaded_context (abbreviated as tc). */
1767ec681f3Smrgvoid
1777ec681f3Smrgutil_idalloc_mt_init_tc(struct util_idalloc_mt *buf)
1787ec681f3Smrg{
1797ec681f3Smrg   util_idalloc_mt_init(buf, 1 << 16, true);
1807ec681f3Smrg}
1817ec681f3Smrg
1827ec681f3Smrgvoid
1837ec681f3Smrgutil_idalloc_mt_fini(struct util_idalloc_mt *buf)
1847ec681f3Smrg{
1857ec681f3Smrg   util_idalloc_fini(&buf->buf);
1867ec681f3Smrg   simple_mtx_destroy(&buf->mutex);
1877ec681f3Smrg}
1887ec681f3Smrg
1897ec681f3Smrgunsigned
1907ec681f3Smrgutil_idalloc_mt_alloc(struct util_idalloc_mt *buf)
1917ec681f3Smrg{
1927ec681f3Smrg   simple_mtx_lock(&buf->mutex);
1937ec681f3Smrg   unsigned id = util_idalloc_alloc(&buf->buf);
1947ec681f3Smrg   simple_mtx_unlock(&buf->mutex);
1957ec681f3Smrg   return id;
1967ec681f3Smrg}
1977ec681f3Smrg
1987ec681f3Smrgvoid
1997ec681f3Smrgutil_idalloc_mt_free(struct util_idalloc_mt *buf, unsigned id)
2007ec681f3Smrg{
2017ec681f3Smrg   if (id == 0 && buf->skip_zero)
2027ec681f3Smrg      return;
2037ec681f3Smrg
2047ec681f3Smrg   simple_mtx_lock(&buf->mutex);
2057ec681f3Smrg   util_idalloc_free(&buf->buf, id);
2067ec681f3Smrg   simple_mtx_unlock(&buf->mutex);
2077ec681f3Smrg}
208