log(hmap_size) (он же log(max_streams)) нижних бит идентификатора стрима. Низлежащий индексируемый массив будет состоять из структуры, включающей в себя целый ид стрима, и непосредственно канал для связи с горутиной. Речь идёт о трёхзначных числах.Интуитивно решается вопрос с зацикливанием - новые вхождения будут сами по себе постепенно переползать с конца мапы в начало. С пропуском всё тоже прекрасно решается, правда, разрешение коллизий линейной пробой может быть больноватым. Например, если у нас завершились два самых новых стрима, а все остальные всё ещё активны - мы обойдём весь массив прежде, чем найдётся свободный слот. Лукап будет не менее болезненным.
В общем-то, даже этот худший кейс можно практически за бесценок амортизировать. Рядом держать метадату из hmap_size битов, где у свободных слотов биты включены. Тогда линейная проба при вставке будет очень дешёвой, но всё ещё остаётся проблема с лукапом.
Ну, правда, и проблемой-то это назвать можно с натяжкой. Принимая за базовый случай 128 вхождений, перебор ~100 uint32 не так уж и дорого, как для worst case. Больновато, но это 30-150нс, если массив нормально в кэше лежит. Беря 8 байт на вхождение (uint32 идентификатор стрима + указатель на канал = 2 машинных слова, рассматриваем 64-разрядные системы), 1024 байт на таблицу выглядит вполне кэшебабельно. Не предел мечтаний, конечно, но всё ещё крайне вероятно оптимальнее стд мапы, где свиссмапа на свиссмапе (буквально, кстати).
Наконец-то применяю эти знания. Не зря ресёрчил.
Утка.