以前的这篇文章介绍了嵌入的变长数据结构(embeded)
本文介绍一个使用这种思想实现的通用strmap容器,相当于:
std::map<std::string, Mapped>。
实现上使用了我以前写的线索红黑树——相比标准map的实现,节省了一半的结点存储开销,而平均查找时间只付出很小的额外开销,并且没有代码膨胀问题。
使用
大多数情况下
trbstrmap<MappedType[, Compare]> // Compare是可选的,allocator 总在类范围,可以修改:
trbstrmap<MappedType[, Compare]>::s_vtab.alloc = ....;
为了节省空间,trbstrmap使用一个字节存储strkey_length,因此,strlen(strkey)不可超过255,允许长度为0的key。最大长度255基本上可以应对绝大多数情况。
把key作为变长字段,有一个原因很重要:key一旦插入,就不可改变!因此,整个结点的memblock也就不需要重新分配,可以修改的只有mapped_data。
作为一个比较:如果把key作为一个固定长度的结构,而mapped_data作为变长内嵌数据,当需要修改mapped_data时,就需要重新分配该结点,还需要修改该结点的父节点指向该结点的指针,并且,会破坏“node容器的iterator只有在删除该node以后才失效”的语义!
一旦用在多线程环境中,问题就会变得很复杂,因为修改了父节点。就不能仅lock需要修改数据的那个结点,而是需要lock整个tree,使用数据库的术语,就是一下子从记录锁变成了表锁,本来按key查找时,因为key没改变,就不需要加锁,而这样搞,查找时就需要对整个表加锁,严重影响并发性!
注意事项:
1. trbstrmap::iterator::value_type 和trbstrmap::value_type都是 mapped_type/Value,*iter也是mapped_type;而不是std::map的pair<Key,Value>。
2. trbstrmap::key(iter)获得iter对应的key
3. trbstrmap::klen(iter)获得key_length,当然,strlen(trbstrmap::key(iter))也可以获得key_length,但需要一些时间开销;另外,当key_content中存在\0时,klen就是不可缺少的了(后面将详细介绍这种情况)
4. 再次强调,key_cstr_length最大长度不可超过255,但这个长度不包括末尾的 \0
示例代码:
typedef febird::trbstrmap<int, &trb_compare_tev_inline_cstr_nocase> smap_t;
smap_t smap, smap2;
smap["1"] = 0;
smap["2"] = 1;
smap["3"] = 1;
smap["4"] = 2;
smap["1"]++;
smap["4"] += 2;
smap.insert("5", 5);
assert(!smap.insert("5", 6).second);
// str长度不可超过255
smap["aabbccddeeffgghhiijjkk====================================="] = 10;
for (typename SMT::iterator i = smap.begin(); i != smap.end(); ++i)
{
printf("smap[%s]=%d, klen=%d\n", smap_t::key(i), *i, smap_t::klen(i));
}
在简单的情况下,使用trbstrmap,比std::map可以节省50%~70%的内存。使用的方便程度,基本上没差别。
数据结构
Mapped Data 是模板参数,可以是任意struct/class,当然也可以内嵌指针,也可以是复杂对象,没有任何限制。
复杂情形:任意字符串,字符串中可以包含\0
trbstrmap允许加入内容中存在\0的字符串,这种应用比较罕见,但是仍然允许,只是必须遵循以下注意事项:
1. 在这种情况下,需要传入key_content_length参数:
trbstrmap.probe(key,key_content_length)
trbstrmap.insert(key,key_content_length, val)
key_content_length是整个key_content长度,包括末尾的\0——如果有的话
2. 或者,传入std::string :
trbstrmap[std::string(…)] = val;
equal to: trbstrmap.probe(s.c_str(), s.size()+1) = val;
3. trbstrmap.insert(key, klen, val)
4. 这种情况下,应用一般需要传入一个自定义的Compare
5. trbstrmap::klen(iter)在任何情况下,返回的都是key_content_length – 1,
而不管末尾有没有\0
6. 自定义函数需要获取key_content_length时,应该如下:
int
trb_compare_custom ( const struct trb_vtab* TRB_restrict vtab,
const struct trb_tree* TRB_restrict tree,
const void* TRB_restrict x,
const void* TRB_restrict y)
{
size_t lx = ((unsigned char*)x)[-1] + 1;
size_t ly = ((unsigned char*)y)[-1] + 1;
int ret = memcmp(x, y, lx<ly?lx:ly); // 仅示例,不一定非用memcmp
return 0 == ret ? lx – ly : ret;
}
7. 当给trbstrmap::find/lower_bound/equal_range多传入一个key_content_length时,这些函数会将key和key_content_length打包(这些都在库内部完成),应用须知这会有稍微多一些的开销。
必须这么做,因为compare函数需要找到key_content_length,除此之外,别无他法。
8. 貌似很复杂,但始终坚持剃刀原理:在绝大多数情况下,也就是字符串是cstr,末尾包含\0时,函数的行为符合传统惯例并且高效;在罕见情况下也可用,只是效率有一点下降,使用难度也大一些。
9. 当情况复杂时,记住:只要你明确传入了长度,这个长度就是你真实数据的长度
测试代码:
- 大小: 17.7 KB
分享到:
相关推荐
The 80x86 MOV Instruction<br>4.8 - Some Final Comments on the MOV Instructions<br><br>4.9 Laboratory Exercises<br>4.9.1 The UCR Standard Library for 80x86 Assembly Language Programmers<br>4.9.2 ...
1,01.zip<br>Output<br>显示所有的调试信息(5KB)<END><br>2,02.zip<br>Some general debugging tips<br>一般的调试技巧(11KB)<END><br>3,03.zip<br>Debugging ISAPI extension<br>调试ISAPI扩展(4KB)<END><br>4,04....
<br> Copyright <br> Praise for Visual Studio Tools for Office <br> Microsoft .NET Development Series <br> Titles in the Series <br> About the Authors <br> Foreword <br> Preface <br> Acknowledgments ...
<br> Copyright <br> Praise for Visual Studio Tools for Office <br> Microsoft .NET Development Series <br> Titles in the Series <br> About the Authors <br> Foreword <br> Preface <br> Acknowledgments ...
1,01.zip<br>Dialogs in DLL<br>在DLL中实现对话框(5KB)<END><br>2,02.zip<br>Export dialogs in MFC Extension DLLs<br>在MFC扩充DLL中输出对话框(12KB)<END><br>3,03.zip<br>Remapping resource script ID's<br>...
1,01.zip<br>Safe file name comparison<br>处理长文件名的比较(5KB)<END><br>2,02.zip<br>Mapped File Class<br>映像文件类(11KB)<END><br>3,03.zip<br>Filename Handling Class <br>有关文件名的类(5KB)<END><br>4...
Applied ADO.NET: Building Data-Driven Solutions 第一部分<br>Table of Contents <br> Applied ADO.NET—Building Data-Driven Solutions <br> Introduction <br> Chapter 1 - ADO.NET Basics <br> Chapter 2 - ...
compassHits可以得到scores,resources和mapped objects.<br><br>Compass也提供了CompassTemplate和CompassCallback类处理会话和事务的处理。CompassTemplate template = new CompassTemplate(compass);<br><br>
specific model.<br><br>Key topics include:<br><br>Software product line engineering process, which extends the Unified Development Software Process to address software product lines<br><br>Use case ...
specific model.<br><br>Key topics include:<br><br>Software product line engineering process, which extends the Unified Development Software Process to address software product lines<br><br>Use case ...
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="info"> <appender-ref ref="STDOUT" /> </root> </configuration> ``` 在这个配置中,...
<br><br>You can obtain equivalent command-line tools, Handle and ListDLLs, at the Sysinternals Web site.<br><br>Process Explorer does not require administrative privileges to run and works on Windows...
- **`<dynamic>` 标签内的 `<if>`**:虽然示例中未直接出现 `<if>` 标签,但它同样可以嵌入到 `<dynamic>` 标签内,用于更复杂的条件判断。 #### 七、总结 通过本文的介绍,我们了解了 ibatis 中 Dynamic SQL 的...
- **`go<addr><a0><a1><a2><a3>`**:跳转到指定地址执行代码,并可以传递最多四个参数。 - **`dump<addr><length>`**:显示(十六进制形式)一段内存区域。 - **`call<addr><a0><a1><a2><a3>`**:带有返回的跳转至...
6. `<datasource>`元素则用于配置数据源,这是连接到数据库的关键部分,可以配置连接池参数,以优化数据库连接的使用。 7. `<sqlMap>`元素包含了具体的SQL语句映射,每个`<sqlMap>`可以有多个`<statement>`元素,...
Cannot access field <fieldname> as type variant." * TClientDataSet doesn‘t save data to file when FileName is set and there is no existing file on disk (Quality Central 2307). * Using the Delphi...
<artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ...
asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>> syncMethod<T, U>(action: Action<T>): Action<U> ``` 同时,该类还具有两个非函数属性:`count` 和 `message`。问题的要求是编写一个名为 `connect` 的...
LOG4J2的生产环境配置配置案例: 4.日志滚动,避免单个日志过大,可以按小时进行日志分割. ...<Pattern>%d %p %c{1.} [%t] %m%n</Pattern> </PatternLayout> <TimeBasedTriggeringPolicy /> </RollingFile>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> ``` **过滤器** 过滤器允许根据日志级别、MDC(Mapped Diagnostic Context)、日志消息内容等条件过滤...