From ce87e0450832c85f1f4170faf936954f7df16ce7 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Tue, 10 Mar 2026 19:32:36 +0800 Subject: [PATCH 01/32] chore: remove unused keys when processing index/ordered-index --- .../indexes/generator.go | 22 +++++++++++++++ .../indexes/index.go | 4 ++- .../indexes/ordered_index.go | 4 ++- .../indexes/generator.go | 22 +++++++++++++++ .../indexes/index.go | 7 +++-- .../indexes/orderedindex.go | 7 +++-- .../src/protoconf/index_conf.pc.cc | 7 ----- .../src/protoconf/item_conf.pc.cc | 2 -- .../src/protoconf/test_conf.pc.cc | 3 -- .../protoconf/loader/hero_conf.pc.go | 4 +-- .../protoconf/loader/index_conf.pc.go | 28 +++++-------------- .../protoconf/loader/item_conf.pc.go | 6 ++-- .../protoconf/loader/test_conf.pc.go | 11 ++------ 13 files changed, 73 insertions(+), 54 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index b6f821fe..b5b0a76e 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -124,3 +124,25 @@ func (x *Generator) GenCppIndexFinders() { x.genCppIndexFinders() x.genCppOrderedIndexFinders() } + +// needMapKeyForIndex checks if the map key variable at the given mapDepth +// is needed by any subsequent regular index's leveled containers. +func (x *Generator) needMapKeyForIndex(mapDepth int) bool { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + if len(levelMessage.Indexes) > 0 && levelMessage.MapDepth-2 >= mapDepth { + return true + } + } + return false +} + +// needMapKeyForOrderedIndex checks if the map key variable at the given mapDepth +// is needed by any subsequent ordered index's leveled containers. +func (x *Generator) needMapKeyForOrderedIndex(mapDepth int) bool { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + if len(levelMessage.OrderedIndexes) > 0 && levelMessage.MapDepth-2 >= mapDepth { + return true + } + } + return false +} diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index 62835c99..0f014005 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -169,7 +169,9 @@ func (x *Generator) genIndexLoader() { x.g.P(helper.Indent(levelMessage.Depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") parentDataName = itemName if levelMessage.FD.IsMap() { - x.g.P(helper.Indent(levelMessage.Depth+1), "auto k", levelMessage.MapDepth, " = ", itemName, ".first;") + if x.needMapKeyForIndex(levelMessage.MapDepth) { + x.g.P(helper.Indent(levelMessage.Depth+1), "auto k", levelMessage.MapDepth, " = ", itemName, ".first;") + } parentDataName = itemName + ".second" } defer x.g.P(helper.Indent(levelMessage.Depth), "}") diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index 78caca55..6b5fadb4 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -159,7 +159,9 @@ func (x *Generator) genOrderedIndexLoader() { x.g.P(helper.Indent(levelMessage.Depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") parentDataName = itemName if levelMessage.FD.IsMap() { - x.g.P(helper.Indent(levelMessage.Depth+1), "auto k", levelMessage.MapDepth, " = ", itemName, ".first;") + if x.needMapKeyForOrderedIndex(levelMessage.MapDepth) { + x.g.P(helper.Indent(levelMessage.Depth+1), "auto k", levelMessage.MapDepth, " = ", itemName, ".first;") + } parentDataName = itemName + ".second" } defer x.g.P(helper.Indent(levelMessage.Depth), "}") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index 6ce60493..ebdf48db 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -119,3 +119,25 @@ func (x *Generator) GenIndexFinders() { x.genIndexFinders() x.genOrderedIndexFinders() } + +// needMapKeyForIndex checks if the map key variable at the given mapDepth +// is needed by any subsequent regular index's leveled containers. +func (x *Generator) needMapKeyForIndex(mapDepth int) bool { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + if len(levelMessage.Indexes) > 0 && levelMessage.MapDepth-2 >= mapDepth { + return true + } + } + return false +} + +// needMapKeyForOrderedIndex checks if the map key variable at the given mapDepth +// is needed by any subsequent ordered index's leveled containers. +func (x *Generator) needMapKeyForOrderedIndex(mapDepth int) bool { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + if len(levelMessage.OrderedIndexes) > 0 && levelMessage.MapDepth-2 >= mapDepth { + return true + } + } + return false +} diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index c464ff60..d4bdf648 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -134,8 +134,11 @@ func (x *Generator) genIndexLoader() { break } if levelMessage.FD.IsMap() { - x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") - x.g.P("_ = ", keyName) + if x.needMapKeyForIndex(levelMessage.MapDepth) { + x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + } else { + x.g.P("for _, ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + } } else { x.g.P("for _ , ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") } diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go b/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go index 5ab40268..e2aaf24d 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go @@ -158,8 +158,11 @@ func (x *Generator) genOrderedIndexLoader() { break } if levelMessage.FD.IsMap() { - x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") - x.g.P("_ = ", keyName) + if x.needMapKeyForOrderedIndex(levelMessage.MapDepth) { + x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + } else { + x.g.P("for _, ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + } } else { x.g.P("for _ , ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") } diff --git a/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc index d1607380..3ea0cf8f 100644 --- a/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc @@ -27,7 +27,6 @@ bool FruitConf::ProcessAfterLoad() { for (auto&& item1 : data_.fruit_map()) { auto k1 = item1.first; for (auto&& item2 : item1.second.item_map()) { - auto k2 = item2.first; { // OrderedIndex: Price ordered_index_item_map_[item2.second.price()].push_back(&item2.second); @@ -141,7 +140,6 @@ bool Fruit2Conf::ProcessAfterLoad() { index_country_map_[item2.name()].push_back(&item2); } for (auto&& item3 : item2.item_map()) { - auto k2 = item3.first; for (auto&& item4 : item3.second.attr_list()) { { // Index: CountryItemAttrName @@ -159,7 +157,6 @@ bool Fruit2Conf::ProcessAfterLoad() { auto k1 = item1.first; for (auto&& item2 : item1.second.country_list()) { for (auto&& item3 : item2.item_map()) { - auto k2 = item3.first; { // OrderedIndex: CountryItemPrice ordered_index_item_map_[item3.second.price()].push_back(&item3.second); @@ -326,7 +323,6 @@ bool Fruit3Conf::ProcessAfterLoad() { index_country_map_[item2.name()].push_back(&item2); } for (auto&& item3 : item2.item_map()) { - auto k1 = item3.first; for (auto&& item4 : item3.second.attr_list()) { { // Index: CountryItemAttrName @@ -341,7 +337,6 @@ bool Fruit3Conf::ProcessAfterLoad() { for (auto&& item1 : data_.fruit_list()) { for (auto&& item2 : item1.country_list()) { for (auto&& item3 : item2.item_map()) { - auto k1 = item3.first; { // OrderedIndex: CountryItemPrice ordered_index_item_map_[item3.second.price()].push_back(&item3.second); @@ -444,7 +439,6 @@ bool Fruit4Conf::ProcessAfterLoad() { index_country_map1_[k1][item2.second.name()].push_back(&item2.second); } for (auto&& item3 : item2.second.item_map()) { - auto k3 = item3.first; for (auto&& item4 : item3.second.attr_list()) { { // Index: CountryItemAttrName @@ -465,7 +459,6 @@ bool Fruit4Conf::ProcessAfterLoad() { for (auto&& item2 : item1.second.country_map()) { auto k2 = item2.first; for (auto&& item3 : item2.second.item_map()) { - auto k3 = item3.first; { // OrderedIndex: CountryItemPrice ordered_index_item_map_[item3.second.price()].push_back(&item3.second); diff --git a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc index 156cc9b2..67a91675 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc @@ -38,7 +38,6 @@ bool ItemConf::ProcessAfterLoad() { index_item_path_friend_id_map_.clear(); index_use_effect_type_map_.clear(); for (auto&& item1 : data_.item_map()) { - auto k1 = item1.first; { // Index: Type index_item_map_[item1.second.type()].push_back(&item1.second); @@ -115,7 +114,6 @@ bool ItemConf::ProcessAfterLoad() { ordered_index_ext_type_map_.clear(); ordered_index_param_ext_type_map_.clear(); for (auto&& item1 : data_.item_map()) { - auto k1 = item1.first; { // OrderedIndex: ExtType@ExtType for (auto&& item2 : item1.second.ext_type_list()) { diff --git a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc index 6388e121..ca4bd15d 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc @@ -66,7 +66,6 @@ bool ActivityConf::ProcessAfterLoad() { index_named_chapter_map1_[k1][item2.second.chapter_name()].push_back(&item2.second); } for (auto&& item3 : item2.second.section_map()) { - auto k3 = item3.first; for (auto&& item4 : item3.second.section_item_list()) { { // Index: SectionItemID@Award @@ -428,7 +427,6 @@ bool TaskConf::ProcessAfterLoad() { // Index init. index_task_map_.clear(); for (auto&& item1 : data_.task_map()) { - auto k1 = item1.first; { // Index: ActivityID index_task_map_[item1.second.activity_id()].push_back(&item1.second); @@ -451,7 +449,6 @@ bool TaskConf::ProcessAfterLoad() { ordered_index_sorted_task_expiry_map_.clear(); ordered_index_activity_expiry_map_.clear(); for (auto&& item1 : data_.task_map()) { - auto k1 = item1.first; { // OrderedIndex: Goal@OrderedTask ordered_index_ordered_task_map_[item1.second.goal()].push_back(&item1.second); diff --git a/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go b/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go index 188558b6..a47a64cc 100644 --- a/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go @@ -97,9 +97,7 @@ func (x *HeroConf) processAfterLoad() error { x.indexAttrMap = make(HeroConf_Index_AttrMap) x.indexAttrMap1 = make(map[string]HeroConf_Index_AttrMap) for k1, v1 := range x.data.GetHeroMap() { - _ = k1 - for k2, v2 := range v1.GetAttrMap() { - _ = k2 + for _, v2 := range v1.GetAttrMap() { { // Index: Title key := v2.GetTitle() diff --git a/test/go-tableau-loader/protoconf/loader/index_conf.pc.go b/test/go-tableau-loader/protoconf/loader/index_conf.pc.go index 03ecab0a..1fc0ad56 100644 --- a/test/go-tableau-loader/protoconf/loader/index_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/index_conf.pc.go @@ -96,9 +96,7 @@ func (x *FruitConf) processAfterLoad() error { x.orderedIndexItemMap = treemap.New[int32, []*protoconf.FruitConf_Fruit_Item]() x.orderedIndexItemMap1 = make(map[int32]*FruitConf_OrderedIndex_ItemMap) for k1, v1 := range x.data.GetFruitMap() { - _ = k1 - for k2, v2 := range v1.GetItemMap() { - _ = k2 + for _, v2 := range v1.GetItemMap() { { // OrderedIndex: Price key := v2.GetPrice() @@ -298,15 +296,13 @@ func (x *Fruit2Conf) processAfterLoad() error { x.indexAttrMap = make(Fruit2Conf_Index_AttrMap) x.indexAttrMap1 = make(map[int32]Fruit2Conf_Index_AttrMap) for k1, v1 := range x.data.GetFruitMap() { - _ = k1 for _, v2 := range v1.GetCountryList() { { // Index: CountryName key := v2.GetName() x.indexCountryMap[key] = append(x.indexCountryMap[key], v2) } - for k2, v3 := range v2.GetItemMap() { - _ = k2 + for _, v3 := range v2.GetItemMap() { for _, v4 := range v3.GetAttrList() { { // Index: CountryItemAttrName @@ -325,10 +321,8 @@ func (x *Fruit2Conf) processAfterLoad() error { x.orderedIndexItemMap = treemap.New[int32, []*protoconf.Fruit2Conf_Fruit_Country_Item]() x.orderedIndexItemMap1 = make(map[int32]*Fruit2Conf_OrderedIndex_ItemMap) for k1, v1 := range x.data.GetFruitMap() { - _ = k1 for _, v2 := range v1.GetCountryList() { - for k2, v3 := range v2.GetItemMap() { - _ = k2 + for _, v3 := range v2.GetItemMap() { { // OrderedIndex: CountryItemPrice key := v3.GetPrice() @@ -586,8 +580,7 @@ func (x *Fruit3Conf) processAfterLoad() error { key := v2.GetName() x.indexCountryMap[key] = append(x.indexCountryMap[key], v2) } - for k1, v3 := range v2.GetItemMap() { - _ = k1 + for _, v3 := range v2.GetItemMap() { for _, v4 := range v3.GetAttrList() { { // Index: CountryItemAttrName @@ -602,8 +595,7 @@ func (x *Fruit3Conf) processAfterLoad() error { x.orderedIndexItemMap = treemap.New[int32, []*protoconf.Fruit3Conf_Fruit_Country_Item]() for _, v1 := range x.data.GetFruitList() { for _, v2 := range v1.GetCountryList() { - for k1, v3 := range v2.GetItemMap() { - _ = k1 + for _, v3 := range v2.GetItemMap() { { // OrderedIndex: CountryItemPrice key := v3.GetPrice() @@ -796,9 +788,7 @@ func (x *Fruit4Conf) processAfterLoad() error { x.indexAttrMap1 = make(map[int32]Fruit4Conf_Index_AttrMap) x.indexAttrMap2 = make(map[Fruit4Conf_LevelIndex_Fruit_CountryKey]Fruit4Conf_Index_AttrMap) for k1, v1 := range x.data.GetFruitMap() { - _ = k1 for k2, v2 := range v1.GetCountryMap() { - _ = k2 { // Index: CountryName key := v2.GetName() @@ -808,8 +798,7 @@ func (x *Fruit4Conf) processAfterLoad() error { } x.indexCountryMap1[k1][key] = append(x.indexCountryMap1[k1][key], v2) } - for k3, v3 := range v2.GetItemMap() { - _ = k3 + for _, v3 := range v2.GetItemMap() { for _, v4 := range v3.GetAttrList() { { // Index: CountryItemAttrName @@ -834,11 +823,8 @@ func (x *Fruit4Conf) processAfterLoad() error { x.orderedIndexItemMap1 = make(map[int32]*Fruit4Conf_OrderedIndex_ItemMap) x.orderedIndexItemMap2 = make(map[Fruit4Conf_LevelIndex_Fruit_CountryKey]*Fruit4Conf_OrderedIndex_ItemMap) for k1, v1 := range x.data.GetFruitMap() { - _ = k1 for k2, v2 := range v1.GetCountryMap() { - _ = k2 - for k3, v3 := range v2.GetItemMap() { - _ = k3 + for _, v3 := range v2.GetItemMap() { { // OrderedIndex: CountryItemPrice key := v3.GetPrice() diff --git a/test/go-tableau-loader/protoconf/loader/item_conf.pc.go b/test/go-tableau-loader/protoconf/loader/item_conf.pc.go index d63807dc..86b117d5 100644 --- a/test/go-tableau-loader/protoconf/loader/item_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/item_conf.pc.go @@ -179,8 +179,7 @@ func (x *ItemConf) processAfterLoad() error { x.indexItemPathNameMap = make(ItemConf_Index_ItemPathNameMap) x.indexItemPathFriendIdMap = make(ItemConf_Index_ItemPathFriendIDMap) x.indexUseEffectTypeMap = make(ItemConf_Index_UseEffectTypeMap) - for k1, v1 := range x.data.GetItemMap() { - _ = k1 + for _, v1 := range x.data.GetItemMap() { { // Index: Type key := v1.GetType() @@ -266,8 +265,7 @@ func (x *ItemConf) processAfterLoad() error { // OrderedIndex init. x.orderedIndexExtTypeMap = treemap.New[protoconf.FruitType, []*protoconf.ItemConf_Item]() x.orderedIndexParamExtTypeMap = treemap.New2[ItemConf_OrderedIndex_ParamExtTypeKey, []*protoconf.ItemConf_Item]() - for k1, v1 := range x.data.GetItemMap() { - _ = k1 + for _, v1 := range x.data.GetItemMap() { { // OrderedIndex: ExtType@ExtType for _, v2 := range v1.GetExtTypeList() { diff --git a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go index 671f0ccd..9bcec3bd 100644 --- a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go @@ -167,14 +167,12 @@ func (x *ActivityConf) processAfterLoad() error { x.indexAwardMap1 = make(map[uint64]ActivityConf_Index_AwardMap) x.indexAwardMap2 = make(map[ActivityConf_LevelIndex_Activity_ChapterKey]ActivityConf_Index_AwardMap) for k1, v1 := range x.data.GetActivityMap() { - _ = k1 { // Index: ActivityName key := v1.GetActivityName() x.indexActivityMap[key] = append(x.indexActivityMap[key], v1) } for k2, v2 := range v1.GetChapterMap() { - _ = k2 { // Index: ChapterID key := v2.GetChapterId() @@ -193,8 +191,7 @@ func (x *ActivityConf) processAfterLoad() error { } x.indexNamedChapterMap1[k1][key] = append(x.indexNamedChapterMap1[k1][key], v2) } - for k3, v3 := range v2.GetSectionMap() { - _ = k3 + for _, v3 := range v2.GetSectionMap() { for _, v4 := range v3.GetSectionItemList() { { // Index: SectionItemID@Award @@ -788,8 +785,7 @@ func (x *TaskConf) originalMessage() proto.Message { func (x *TaskConf) processAfterLoad() error { // Index init. x.indexTaskMap = make(TaskConf_Index_TaskMap) - for k1, v1 := range x.data.GetTaskMap() { - _ = k1 + for _, v1 := range x.data.GetTaskMap() { { // Index: ActivityID key := v1.GetActivityId() @@ -813,8 +809,7 @@ func (x *TaskConf) processAfterLoad() error { x.orderedIndexTaskExpiryMap = treemap.New[int64, []*protoconf.TaskConf_Task]() x.orderedIndexSortedTaskExpiryMap = treemap.New[int64, []*protoconf.TaskConf_Task]() x.orderedIndexActivityExpiryMap = treemap.New2[TaskConf_OrderedIndex_ActivityExpiryKey, []*protoconf.TaskConf_Task]() - for k1, v1 := range x.data.GetTaskMap() { - _ = k1 + for _, v1 := range x.data.GetTaskMap() { { // OrderedIndex: Goal@OrderedTask key := v1.GetGoal() From 2535b762c740c485c005c3a5c97e0e27c82d47ad Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Tue, 10 Mar 2026 20:34:47 +0800 Subject: [PATCH 02/32] feat: add new function NumLeveledContainers --- .../indexes/generator.go | 6 ++-- .../indexes/index.go | 36 ++++++++++--------- .../indexes/ordered_index.go | 36 ++++++++++--------- .../indexes/generator.go | 6 ++-- .../indexes/index.go | 33 ++++++++--------- .../{orderedindex.go => ordered_index.go} | 33 ++++++++--------- internal/index/descriptor.go | 11 ++++++ 7 files changed, 91 insertions(+), 70 deletions(-) rename cmd/protoc-gen-go-tableau-loader/indexes/{orderedindex.go => ordered_index.go} (91%) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index b5b0a76e..5a63dd74 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -84,6 +84,8 @@ func (x *Generator) GenHppIndexFinders() { if !x.NeedGenerate() { return } + // maxDepth-3: since maxDepth is the MapDepth of the deepest index level, and LevelIndex keys + // start from the 2nd map level onward, the number of LevelIndex key types is (maxDepth - 2 - 1) = maxDepth - 3. for i := 1; i <= x.maxDepth-3 && i <= len(x.mapFds)-1; i++ { if i == 1 { x.g.P() @@ -129,7 +131,7 @@ func (x *Generator) GenCppIndexFinders() { // is needed by any subsequent regular index's leveled containers. func (x *Generator) needMapKeyForIndex(mapDepth int) bool { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.Indexes) > 0 && levelMessage.MapDepth-2 >= mapDepth { + if len(levelMessage.Indexes) > 0 && levelMessage.NumLeveledContainers() >= mapDepth { return true } } @@ -140,7 +142,7 @@ func (x *Generator) needMapKeyForIndex(mapDepth int) bool { // is needed by any subsequent ordered index's leveled containers. func (x *Generator) needMapKeyForOrderedIndex(mapDepth int) bool { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.OrderedIndexes) > 0 && levelMessage.MapDepth-2 >= mapDepth { + if len(levelMessage.OrderedIndexes) > 0 && levelMessage.NumLeveledContainers() >= mapDepth { return true } } diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index 0f014005..184e23b0 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -104,7 +104,7 @@ func (x *Generator) genHppIndexFinders() { x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -122,7 +122,7 @@ func (x *Generator) genHppIndexFinders() { x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index, 0), ";") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -146,7 +146,7 @@ func (x *Generator) genIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P(helper.Indent(1), x.indexContainerName(index, 0), ".clear();") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -157,7 +157,7 @@ func (x *Generator) genIndexLoader() { parentDataName := "data_" for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { - x.genOneCppIndexLoader(levelMessage.MapDepth, levelMessage.Depth, index, parentDataName) + x.genOneCppIndexLoader(levelMessage, index, parentDataName) } itemName := fmt.Sprintf("item%d", levelMessage.Depth) if levelMessage.FD == nil { @@ -178,7 +178,8 @@ func (x *Generator) genIndexLoader() { } } -func (x *Generator) genOneCppIndexLoader(depth int, ident int, index *index.LevelIndex, parentDataName string) { +func (x *Generator) genOneCppIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { + ident := levelMessage.Depth x.g.P(helper.Indent(ident), "{") x.g.P(helper.Indent(ident+1), "// Index: ", index.Index) if len(index.ColFields) == 1 { @@ -186,31 +187,31 @@ func (x *Generator) genOneCppIndexLoader(depth int, ident int, index *index.Leve field := index.ColFields[0] // just take the first field fieldName, _ := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) + itemName := fmt.Sprintf("item%d", levelMessage.MapDepth) x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") key := itemName if field.FD.Enum() != nil { key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } - x.genLoader(depth, ident+2, index, key, parentDataName) + x.genLoader(levelMessage, index, ident+2, key, parentDataName) x.g.P(helper.Indent(ident+1), "}") } else { key := parentDataName + fieldName - x.genLoader(depth, ident+1, index, key, parentDataName) + x.genLoader(levelMessage, index, ident+1, key, parentDataName) } } else { // multi-column index - x.generateOneCppMulticolumnIndex(depth, ident, index, parentDataName, nil) + x.generateOneCppMulticolumnIndex(levelMessage, index, ident, parentDataName, nil) } x.g.P(helper.Indent(ident), "}") } -func (x *Generator) generateOneCppMulticolumnIndex(depth, ident int, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { +func (x *Generator) generateOneCppMulticolumnIndex(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, parentDataName string, keys helper.MapKeySlice) { cursor := len(keys) if cursor >= len(index.ColFields) { keyType := x.indexMapKeyType(index) x.g.P(helper.Indent(ident+1), keyType, " key{", keys.GenGetArguments(), "};") - x.genLoader(depth, ident+1, index, "key", parentDataName) + x.genLoader(levelMessage, index, ident+1, "key", parentDataName) return } field := index.ColFields[cursor] @@ -223,18 +224,18 @@ func (x *Generator) generateOneCppMulticolumnIndex(depth, ident int, index *inde key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnIndex(depth, ident+1, index, parentDataName, keys) + x.generateOneCppMulticolumnIndex(levelMessage, index, ident+1, parentDataName, keys) x.g.P(helper.Indent(ident+1), "}") } else { key := parentDataName + fieldName keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnIndex(depth, ident, index, parentDataName, keys) + x.generateOneCppMulticolumnIndex(levelMessage, index, ident, parentDataName, keys) } } -func (x *Generator) genLoader(depth, ident int, index *index.LevelIndex, key, parentDataName string) { +func (x *Generator) genLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.indexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") - for i := 1; i <= depth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -275,7 +276,8 @@ func (x *Generator) genIndexSorter() { x.g.P(helper.Indent(1), "for (auto&& item : ", indexContainerName, ") {") x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(1), "}") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + // Iterate all leveled containers. + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -330,7 +332,7 @@ func (x *Generator) genCppIndexFinders() { x.g.P("}") x.g.P() - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index 6b5fadb4..6a968fc9 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -94,7 +94,7 @@ func (x *Generator) genHppOrderedIndexFinders() { x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -112,7 +112,7 @@ func (x *Generator) genHppOrderedIndexFinders() { x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.orderedIndexContainerName(index, 0), ";") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -136,7 +136,7 @@ func (x *Generator) genOrderedIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, 0), ".clear();") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -147,7 +147,7 @@ func (x *Generator) genOrderedIndexLoader() { parentDataName := "data_" for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { - x.genOneCppOrderedIndexLoader(levelMessage.MapDepth, levelMessage.Depth, index, parentDataName) + x.genOneCppOrderedIndexLoader(levelMessage, index, parentDataName) } itemName := fmt.Sprintf("item%d", levelMessage.Depth) if levelMessage.FD == nil { @@ -168,7 +168,8 @@ func (x *Generator) genOrderedIndexLoader() { } } -func (x *Generator) genOneCppOrderedIndexLoader(depth int, ident int, index *index.LevelIndex, parentDataName string) { +func (x *Generator) genOneCppOrderedIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { + ident := levelMessage.Depth x.g.P(helper.Indent(ident), "{") x.g.P(helper.Indent(ident+1), "// OrderedIndex: ", index.Index) if len(index.ColFields) == 1 { @@ -176,31 +177,31 @@ func (x *Generator) genOneCppOrderedIndexLoader(depth int, ident int, index *ind field := index.ColFields[0] // just take the first field fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) + itemName := fmt.Sprintf("item%d", levelMessage.MapDepth) x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") key := itemName + suffix if field.FD.Enum() != nil { key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } - x.genOrderedLoader(depth, ident+2, index, key, parentDataName) + x.genOrderedLoader(levelMessage, index, ident+2, key, parentDataName) x.g.P(helper.Indent(ident+1), "}") } else { key := parentDataName + fieldName + suffix - x.genOrderedLoader(depth, ident+1, index, key, parentDataName) + x.genOrderedLoader(levelMessage, index, ident+1, key, parentDataName) } } else { // multi-column index - x.generateOneCppMulticolumnOrderedIndex(depth, ident, index, parentDataName, nil) + x.generateOneCppMulticolumnOrderedIndex(levelMessage, index, ident, parentDataName, nil) } x.g.P(helper.Indent(ident), "}") } -func (x *Generator) generateOneCppMulticolumnOrderedIndex(depth, ident int, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { +func (x *Generator) generateOneCppMulticolumnOrderedIndex(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, parentDataName string, keys helper.MapKeySlice) { cursor := len(keys) if cursor >= len(index.ColFields) { keyType := x.orderedIndexMapKeyType(index) x.g.P(helper.Indent(ident+1), keyType, " key{", keys.GenGetArguments(), "};") - x.genOrderedLoader(depth, ident+1, index, "key", parentDataName) + x.genOrderedLoader(levelMessage, index, ident+1, "key", parentDataName) return } field := index.ColFields[cursor] @@ -213,18 +214,18 @@ func (x *Generator) generateOneCppMulticolumnOrderedIndex(depth, ident int, inde key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnOrderedIndex(depth, ident+1, index, parentDataName, keys) + x.generateOneCppMulticolumnOrderedIndex(levelMessage, index, ident+1, parentDataName, keys) x.g.P(helper.Indent(ident+1), "}") } else { key := parentDataName + fieldName + suffix keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnOrderedIndex(depth, ident, index, parentDataName, keys) + x.generateOneCppMulticolumnOrderedIndex(levelMessage, index, ident, parentDataName, keys) } } -func (x *Generator) genOrderedLoader(depth, ident int, index *index.LevelIndex, key, parentDataName string) { +func (x *Generator) genOrderedLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") - for i := 1; i <= depth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -265,7 +266,8 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P(helper.Indent(1), "for (auto&& item : ", indexContainerName, ") {") x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(1), "}") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + // Iterate all leveled containers. + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -320,7 +322,7 @@ func (x *Generator) genCppOrderedIndexFinders() { x.g.P("}") x.g.P() - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index ebdf48db..b0034512 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -87,6 +87,8 @@ func (x *Generator) GenIndexTypeDef() { if !x.NeedGenerate() { return } + // maxDepth-3: since maxDepth is the MapDepth of the deepest index level, and LevelIndex keys + // start from the 2nd map level onward, the number of LevelIndex key types is (maxDepth - 2 - 1) = maxDepth - 3. for i := 1; i <= x.maxDepth-3 && i <= len(x.mapFds)-1; i++ { if i == 1 { x.g.P() @@ -124,7 +126,7 @@ func (x *Generator) GenIndexFinders() { // is needed by any subsequent regular index's leveled containers. func (x *Generator) needMapKeyForIndex(mapDepth int) bool { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.Indexes) > 0 && levelMessage.MapDepth-2 >= mapDepth { + if len(levelMessage.Indexes) > 0 && levelMessage.NumLeveledContainers() >= mapDepth { return true } } @@ -135,7 +137,7 @@ func (x *Generator) needMapKeyForIndex(mapDepth int) bool { // is needed by any subsequent ordered index's leveled containers. func (x *Generator) needMapKeyForOrderedIndex(mapDepth int) bool { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.OrderedIndexes) > 0 && levelMessage.MapDepth-2 >= mapDepth { + if len(levelMessage.OrderedIndexes) > 0 && levelMessage.NumLeveledContainers() >= mapDepth { return true } } diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index d4bdf648..a8c3d124 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -83,7 +83,7 @@ func (x *Generator) genIndexField() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P(x.indexContainerName(index, 0), " ", x.indexMapType(index)) - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -107,7 +107,7 @@ func (x *Generator) genIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P("x.", x.indexContainerName(index, 0), " = make(", x.indexMapType(index), ")") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -123,7 +123,7 @@ func (x *Generator) genIndexLoader() { parentDataName := "x.data" for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { - x.genOneIndexLoader(index, levelMessage.MapDepth, parentDataName) + x.genOneIndexLoader(levelMessage, index, parentDataName) } keyName := fmt.Sprintf("k%d", levelMessage.MapDepth) valueName := fmt.Sprintf("v%d", levelMessage.Depth) @@ -147,7 +147,7 @@ func (x *Generator) genIndexLoader() { } } -func (x *Generator) genOneIndexLoader(index *index.LevelIndex, depth int, parentDataName string) { +func (x *Generator) genOneIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { x.g.P("{") x.g.P("// Index: ", index.Index) if len(index.ColFields) == 1 { @@ -155,28 +155,28 @@ func (x *Generator) genOneIndexLoader(index *index.LevelIndex, depth int, parent field := index.ColFields[0] // just take the first field fieldName, _ := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - valueName := fmt.Sprintf("v%d", depth) + valueName := fmt.Sprintf("v%d", levelMessage.MapDepth) x.g.P("for _ , ", valueName, " := range ", parentDataName, fieldName, " {") x.g.P("key := ", valueName) - x.genIndexLoaderCommon(depth, index, parentDataName) + x.genIndexLoaderCommon(levelMessage, index, parentDataName) x.g.P("}") } else { x.g.P("key := ", parentDataName, fieldName) - x.genIndexLoaderCommon(depth, index, parentDataName) + x.genIndexLoaderCommon(levelMessage, index, parentDataName) } } else { // multi-column index - x.generateOneMulticolumnIndex(depth, index, parentDataName, nil) + x.generateOneMulticolumnIndex(levelMessage, index, parentDataName, nil) } x.g.P("}") } -func (x *Generator) generateOneMulticolumnIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { +func (x *Generator) generateOneMulticolumnIndex(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { cursor := len(keys) if cursor >= len(index.ColFields) { keyType := x.indexMapKeyType(index) x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") - x.genIndexLoaderCommon(depth, index, parentDataName) + x.genIndexLoaderCommon(levelMessage, index, parentDataName) return } field := index.ColFields[cursor] @@ -185,19 +185,19 @@ func (x *Generator) generateOneMulticolumnIndex(depth int, index *index.LevelInd itemName := fmt.Sprintf("indexItem%d", cursor) x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") keys = keys.AddMapKey(helper.MapKey{Name: itemName}) - x.generateOneMulticolumnIndex(depth, index, parentDataName, keys) + x.generateOneMulticolumnIndex(levelMessage, index, parentDataName, keys) x.g.P("}") } else { key := parentDataName + fieldName keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnIndex(depth, index, parentDataName, keys) + x.generateOneMulticolumnIndex(levelMessage, index, parentDataName, keys) } } -func (x *Generator) genIndexLoaderCommon(depth int, index *index.LevelIndex, parentDataName string) { +func (x *Generator) genIndexLoaderCommon(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { indexContainerName := x.indexContainerName(index, 0) x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - for i := 1; i <= depth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -246,7 +246,8 @@ func (x *Generator) genIndexSorter() { x.g.P("for _, itemList := range x.", x.indexContainerName(index, 0), " {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("}") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + // Iterate all leveled containers. + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -303,7 +304,7 @@ func (x *Generator) genIndexFinders() { x.g.P("}") x.g.P() - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go similarity index 91% rename from cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go rename to cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go index e2aaf24d..df451d39 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/orderedindex.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go @@ -107,7 +107,7 @@ func (x *Generator) genOrderedIndexField() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P(x.orderedIndexContainerName(index, 0), " *", x.orderedIndexMapType(index)) - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -131,7 +131,7 @@ func (x *Generator) genOrderedIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P("x.", x.orderedIndexContainerName(index, 0), " = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -147,7 +147,7 @@ func (x *Generator) genOrderedIndexLoader() { parentDataName := "x.data" for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { - x.genOneOrderedIndexLoader(index, levelMessage.MapDepth, parentDataName) + x.genOneOrderedIndexLoader(levelMessage, index, parentDataName) } keyName := fmt.Sprintf("k%d", levelMessage.MapDepth) valueName := fmt.Sprintf("v%d", levelMessage.Depth) @@ -171,7 +171,7 @@ func (x *Generator) genOrderedIndexLoader() { } } -func (x *Generator) genOneOrderedIndexLoader(index *index.LevelIndex, depth int, parentDataName string) { +func (x *Generator) genOneOrderedIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { x.g.P("{") x.g.P("// OrderedIndex: ", index.Index) if len(index.ColFields) == 1 { @@ -179,28 +179,28 @@ func (x *Generator) genOneOrderedIndexLoader(index *index.LevelIndex, depth int, field := index.ColFields[0] // just take the first field fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - valueName := fmt.Sprintf("v%d", depth) + valueName := fmt.Sprintf("v%d", levelMessage.MapDepth) x.g.P("for _ , ", valueName, " := range ", parentDataName, fieldName, " {") x.g.P("key := ", valueName, suffix) - x.genOrderedIndexLoaderCommon(depth, index, parentDataName) + x.genOrderedIndexLoaderCommon(levelMessage, index, parentDataName) x.g.P("}") } else { x.g.P("key := ", parentDataName, fieldName, suffix) - x.genOrderedIndexLoaderCommon(depth, index, parentDataName) + x.genOrderedIndexLoaderCommon(levelMessage, index, parentDataName) } } else { // multi-column index - x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, nil) + x.generateOneMulticolumnOrderedIndex(levelMessage, index, parentDataName, nil) } x.g.P("}") } -func (x *Generator) generateOneMulticolumnOrderedIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { +func (x *Generator) generateOneMulticolumnOrderedIndex(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { cursor := len(keys) if cursor >= len(index.ColFields) { keyType := x.orderedIndexMapKeyType(index) x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") - x.genOrderedIndexLoaderCommon(depth, index, parentDataName) + x.genOrderedIndexLoaderCommon(levelMessage, index, parentDataName) return } field := index.ColFields[cursor] @@ -210,20 +210,20 @@ func (x *Generator) generateOneMulticolumnOrderedIndex(depth int, index *index.L x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") key := itemName + suffix keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, keys) + x.generateOneMulticolumnOrderedIndex(levelMessage, index, parentDataName, keys) x.g.P("}") } else { key := parentDataName + fieldName + suffix keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, keys) + x.generateOneMulticolumnOrderedIndex(levelMessage, index, parentDataName, keys) } } -func (x *Generator) genOrderedIndexLoaderCommon(depth int, index *index.LevelIndex, parentDataName string) { +func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { indexContainerName := x.orderedIndexContainerName(index, 0) x.g.P("value, _ := x.", indexContainerName, ".Get(key)") x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - for i := 1; i <= depth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -276,7 +276,8 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("return true") x.g.P("})") - for i := 1; i <= levelMessage.MapDepth-2; i++ { + // Iterate all leveled containers. + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } @@ -335,7 +336,7 @@ func (x *Generator) genOrderedIndexFinders() { x.g.P("}") x.g.P() - for i := 1; i <= levelMessage.MapDepth-2; i++ { + for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { if i > len(x.keys) { break } diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index e68308ec..f042ddd0 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -57,6 +57,17 @@ type LevelMessage struct { Depth, MapDepth int } +// NumLeveledContainers returns the number of upper-level index containers +// for this level message. It equals MapDepth - 2 because: +// - MapDepth is 1-based (starts from 1 for the first map level) +// - The current level itself is excluded (only parent levels count) +// +// For example, a 3-level nested map (MapDepth=3) has 1 upper-level container (keyed by k1), +// and a 4-level nested map (MapDepth=4) has 2 upper-level containers (keyed by k1 and {k1, k2}). +func (l *LevelMessage) NumLeveledContainers() int { + return l.MapDepth - 2 +} + func (l *LevelMessage) NeedGenIndex() bool { if l == nil { return false From 9398e2826d1518caadead7e39c4c1fb82f249fa3 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Wed, 11 Mar 2026 14:19:40 +0800 Subject: [PATCH 03/32] refactor: set Depth and MapDepth to 0-based --- .../indexes/generator.go | 10 ++-- .../indexes/index.go | 12 ++--- .../indexes/ordered_index.go | 12 ++--- .../indexes/generator.go | 10 ++-- .../indexes/index.go | 6 +-- .../indexes/ordered_index.go | 6 +-- internal/index/descriptor.go | 21 +++++--- internal/index/descriptor_test.go | 52 +++++++++---------- 8 files changed, 67 insertions(+), 62 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index 5a63dd74..e5ac1cf1 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -84,9 +84,9 @@ func (x *Generator) GenHppIndexFinders() { if !x.NeedGenerate() { return } - // maxDepth-3: since maxDepth is the MapDepth of the deepest index level, and LevelIndex keys - // start from the 2nd map level onward, the number of LevelIndex key types is (maxDepth - 2 - 1) = maxDepth - 3. - for i := 1; i <= x.maxDepth-3 && i <= len(x.mapFds)-1; i++ { + // maxDepth-2: since maxDepth is the 0-based MapDepth of the deepest index level, and LevelIndex keys + // start from the 2nd map level onward, the number of LevelIndex key types is (maxDepth - 1 - 1) = maxDepth - 2. + for i := 1; i <= x.maxDepth-2 && i <= len(x.mapFds)-1; i++ { if i == 1 { x.g.P() x.g.P(helper.Indent(1), "// LevelIndex keys.") @@ -131,7 +131,7 @@ func (x *Generator) GenCppIndexFinders() { // is needed by any subsequent regular index's leveled containers. func (x *Generator) needMapKeyForIndex(mapDepth int) bool { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.Indexes) > 0 && levelMessage.NumLeveledContainers() >= mapDepth { + if len(levelMessage.Indexes) > 0 && levelMessage.NumLeveledContainers() > mapDepth { return true } } @@ -142,7 +142,7 @@ func (x *Generator) needMapKeyForIndex(mapDepth int) bool { // is needed by any subsequent ordered index's leveled containers. func (x *Generator) needMapKeyForOrderedIndex(mapDepth int) bool { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.OrderedIndexes) > 0 && levelMessage.NumLeveledContainers() >= mapDepth { + if len(levelMessage.OrderedIndexes) > 0 && levelMessage.NumLeveledContainers() > mapDepth { return true } } diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index 184e23b0..5ecfc14a 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -159,27 +159,27 @@ func (x *Generator) genIndexLoader() { for _, index := range levelMessage.Indexes { x.genOneCppIndexLoader(levelMessage, index, parentDataName) } - itemName := fmt.Sprintf("item%d", levelMessage.Depth) + itemName := fmt.Sprintf("item%d", levelMessage.Depth+1) if levelMessage.FD == nil { break } if !levelMessage.NextLevel.NeedGenIndex() { break } - x.g.P(helper.Indent(levelMessage.Depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") + x.g.P(helper.Indent(levelMessage.Depth+1), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") parentDataName = itemName if levelMessage.FD.IsMap() { if x.needMapKeyForIndex(levelMessage.MapDepth) { - x.g.P(helper.Indent(levelMessage.Depth+1), "auto k", levelMessage.MapDepth, " = ", itemName, ".first;") + x.g.P(helper.Indent(levelMessage.Depth+2), "auto k", levelMessage.MapDepth+1, " = ", itemName, ".first;") } parentDataName = itemName + ".second" } - defer x.g.P(helper.Indent(levelMessage.Depth), "}") + defer x.g.P(helper.Indent(levelMessage.Depth+1), "}") } } func (x *Generator) genOneCppIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { - ident := levelMessage.Depth + ident := levelMessage.Depth + 1 x.g.P(helper.Indent(ident), "{") x.g.P(helper.Indent(ident+1), "// Index: ", index.Index) if len(index.ColFields) == 1 { @@ -187,7 +187,7 @@ func (x *Generator) genOneCppIndexLoader(levelMessage *index.LevelMessage, index field := index.ColFields[0] // just take the first field fieldName, _ := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", levelMessage.MapDepth) + itemName := fmt.Sprintf("item%d", levelMessage.MapDepth+1) x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") key := itemName if field.FD.Enum() != nil { diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index 6a968fc9..a2f35e48 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -149,27 +149,27 @@ func (x *Generator) genOrderedIndexLoader() { for _, index := range levelMessage.OrderedIndexes { x.genOneCppOrderedIndexLoader(levelMessage, index, parentDataName) } - itemName := fmt.Sprintf("item%d", levelMessage.Depth) + itemName := fmt.Sprintf("item%d", levelMessage.Depth+1) if levelMessage.FD == nil { break } if !levelMessage.NextLevel.NeedGenOrderedIndex() { break } - x.g.P(helper.Indent(levelMessage.Depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") + x.g.P(helper.Indent(levelMessage.Depth+1), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") parentDataName = itemName if levelMessage.FD.IsMap() { if x.needMapKeyForOrderedIndex(levelMessage.MapDepth) { - x.g.P(helper.Indent(levelMessage.Depth+1), "auto k", levelMessage.MapDepth, " = ", itemName, ".first;") + x.g.P(helper.Indent(levelMessage.Depth+2), "auto k", levelMessage.MapDepth+1, " = ", itemName, ".first;") } parentDataName = itemName + ".second" } - defer x.g.P(helper.Indent(levelMessage.Depth), "}") + defer x.g.P(helper.Indent(levelMessage.Depth+1), "}") } } func (x *Generator) genOneCppOrderedIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { - ident := levelMessage.Depth + ident := levelMessage.Depth + 1 x.g.P(helper.Indent(ident), "{") x.g.P(helper.Indent(ident+1), "// OrderedIndex: ", index.Index) if len(index.ColFields) == 1 { @@ -177,7 +177,7 @@ func (x *Generator) genOneCppOrderedIndexLoader(levelMessage *index.LevelMessage field := index.ColFields[0] // just take the first field fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", levelMessage.MapDepth) + itemName := fmt.Sprintf("item%d", levelMessage.MapDepth+1) x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") key := itemName + suffix if field.FD.Enum() != nil { diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index b0034512..2cbd84c5 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -87,9 +87,9 @@ func (x *Generator) GenIndexTypeDef() { if !x.NeedGenerate() { return } - // maxDepth-3: since maxDepth is the MapDepth of the deepest index level, and LevelIndex keys - // start from the 2nd map level onward, the number of LevelIndex key types is (maxDepth - 2 - 1) = maxDepth - 3. - for i := 1; i <= x.maxDepth-3 && i <= len(x.mapFds)-1; i++ { + // maxDepth-2: since maxDepth is the 0-based MapDepth of the deepest index level, and LevelIndex keys + // start from the 2nd map level onward, the number of LevelIndex key types is (maxDepth - 1 - 1) = maxDepth - 2. + for i := 1; i <= x.maxDepth-2 && i <= len(x.mapFds)-1; i++ { if i == 1 { x.g.P() x.g.P("// LevelIndex keys.") @@ -126,7 +126,7 @@ func (x *Generator) GenIndexFinders() { // is needed by any subsequent regular index's leveled containers. func (x *Generator) needMapKeyForIndex(mapDepth int) bool { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.Indexes) > 0 && levelMessage.NumLeveledContainers() >= mapDepth { + if len(levelMessage.Indexes) > 0 && levelMessage.NumLeveledContainers() > mapDepth { return true } } @@ -137,7 +137,7 @@ func (x *Generator) needMapKeyForIndex(mapDepth int) bool { // is needed by any subsequent ordered index's leveled containers. func (x *Generator) needMapKeyForOrderedIndex(mapDepth int) bool { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.OrderedIndexes) > 0 && levelMessage.NumLeveledContainers() >= mapDepth { + if len(levelMessage.OrderedIndexes) > 0 && levelMessage.NumLeveledContainers() > mapDepth { return true } } diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index a8c3d124..39746b83 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -125,8 +125,8 @@ func (x *Generator) genIndexLoader() { for _, index := range levelMessage.Indexes { x.genOneIndexLoader(levelMessage, index, parentDataName) } - keyName := fmt.Sprintf("k%d", levelMessage.MapDepth) - valueName := fmt.Sprintf("v%d", levelMessage.Depth) + keyName := fmt.Sprintf("k%d", levelMessage.MapDepth+1) + valueName := fmt.Sprintf("v%d", levelMessage.Depth+1) if levelMessage.FD == nil { break } @@ -155,7 +155,7 @@ func (x *Generator) genOneIndexLoader(levelMessage *index.LevelMessage, index *i field := index.ColFields[0] // just take the first field fieldName, _ := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - valueName := fmt.Sprintf("v%d", levelMessage.MapDepth) + valueName := fmt.Sprintf("v%d", levelMessage.MapDepth+1) x.g.P("for _ , ", valueName, " := range ", parentDataName, fieldName, " {") x.g.P("key := ", valueName) x.genIndexLoaderCommon(levelMessage, index, parentDataName) diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go index df451d39..f2e2196d 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go @@ -149,8 +149,8 @@ func (x *Generator) genOrderedIndexLoader() { for _, index := range levelMessage.OrderedIndexes { x.genOneOrderedIndexLoader(levelMessage, index, parentDataName) } - keyName := fmt.Sprintf("k%d", levelMessage.MapDepth) - valueName := fmt.Sprintf("v%d", levelMessage.Depth) + keyName := fmt.Sprintf("k%d", levelMessage.MapDepth+1) + valueName := fmt.Sprintf("v%d", levelMessage.Depth+1) if levelMessage.FD == nil { break } @@ -179,7 +179,7 @@ func (x *Generator) genOneOrderedIndexLoader(levelMessage *index.LevelMessage, i field := index.ColFields[0] // just take the first field fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - valueName := fmt.Sprintf("v%d", levelMessage.MapDepth) + valueName := fmt.Sprintf("v%d", levelMessage.MapDepth+1) x.g.P("for _ , ", valueName, " := range ", parentDataName, fieldName, " {") x.g.P("key := ", valueName, suffix) x.genOrderedIndexLoaderCommon(levelMessage, index, parentDataName) diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index f042ddd0..10daef58 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -53,19 +53,24 @@ type LevelMessage struct { // Current level message's all index fields Indexes, OrderedIndexes []*LevelIndex - // depth of message hierarchy - Depth, MapDepth int + // Depth is the 0-based depth of message hierarchy. + // For example, the top-level message has Depth=0, the next level has Depth=1, and so on. + Depth int + // MapDepth is the 0-based map depth of message hierarchy. + // It only increments when the next level is a map field. + // For example, the top-level message has MapDepth=0, the next level (if map) has MapDepth=1, and so on. + MapDepth int } // NumLeveledContainers returns the number of upper-level index containers -// for this level message. It equals MapDepth - 2 because: -// - MapDepth is 1-based (starts from 1 for the first map level) +// for this level message. It equals MapDepth - 1 because: +// - MapDepth is 0-based (starts from 0 for the top-level message) // - The current level itself is excluded (only parent levels count) // -// For example, a 3-level nested map (MapDepth=3) has 1 upper-level container (keyed by k1), -// and a 4-level nested map (MapDepth=4) has 2 upper-level containers (keyed by k1 and {k1, k2}). +// For example, a 3-level nested map (MapDepth=2) has 1 upper-level container (keyed by k1), +// and a 4-level nested map (MapDepth=3) has 2 upper-level containers (keyed by k1 and {k1, k2}). func (l *LevelMessage) NumLeveledContainers() int { - return l.MapDepth - 2 + return l.MapDepth - 1 } func (l *LevelMessage) NeedGenIndex() bool { @@ -193,7 +198,7 @@ func parseCols(cols []string, prefix string, md protoreflect.MessageDescriptor, func ParseIndexDescriptor(md protoreflect.MessageDescriptor) *IndexDescriptor { descriptor := &IndexDescriptor{ - LevelMessage: parseLevelMessage(md, 1, 1), + LevelMessage: parseLevelMessage(md, 0, 0), } indexes, orderedIndexes := ParseWSOptionIndex(md) // parse indexes into level message diff --git a/internal/index/descriptor_test.go b/internal/index/descriptor_test.go index aa5e0e29..e359189d 100644 --- a/internal/index/descriptor_test.go +++ b/internal/index/descriptor_test.go @@ -34,12 +34,12 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 1, - MapDepth: 1, + Depth: 0, + MapDepth: 0, FD: fd[*protoconf.ItemConf]("item_map"), NextLevel: &LevelMessage{ - Depth: 2, - MapDepth: 2, + Depth: 1, + MapDepth: 1, Indexes: []*LevelIndex{ { Index: &Index{ @@ -304,16 +304,16 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 1, - MapDepth: 1, + Depth: 0, + MapDepth: 0, FD: fd[*protoconf.HeroConf]("hero_map"), NextLevel: &LevelMessage{ - Depth: 2, - MapDepth: 2, + Depth: 1, + MapDepth: 1, FD: fd[*protoconf.HeroConf_Hero]("attr_map"), NextLevel: &LevelMessage{ - Depth: 3, - MapDepth: 3, + Depth: 2, + MapDepth: 2, Indexes: []*LevelIndex{ { Index: &Index{ @@ -343,12 +343,12 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 1, - MapDepth: 1, + Depth: 0, + MapDepth: 0, FD: fd[*protoconf.ActivityConf]("activity_map"), NextLevel: &LevelMessage{ - Depth: 2, - MapDepth: 2, + Depth: 1, + MapDepth: 1, FD: fd[*protoconf.ActivityConf_Activity]("chapter_map"), Indexes: []*LevelIndex{ { @@ -368,8 +368,8 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, NextLevel: &LevelMessage{ - Depth: 3, - MapDepth: 3, + Depth: 2, + MapDepth: 2, FD: fd[*protoconf.ActivityConf_Activity_Chapter]("section_map"), Indexes: []*LevelIndex{ { @@ -413,12 +413,12 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, NextLevel: &LevelMessage{ - Depth: 4, - MapDepth: 4, + Depth: 3, + MapDepth: 3, FD: fd[*protoconf.Section]("section_item_list"), NextLevel: &LevelMessage{ - Depth: 5, - MapDepth: 4, + Depth: 4, + MapDepth: 3, FD: fd[*protoconf.Section_SectionItem]("decompose_item_list"), Indexes: []*LevelIndex{ { @@ -438,8 +438,8 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, NextLevel: &LevelMessage{ - Depth: 6, - MapDepth: 4, + Depth: 5, + MapDepth: 3, }, }, }, @@ -455,12 +455,12 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 1, - MapDepth: 1, + Depth: 0, + MapDepth: 0, FD: fd[*protoconf.TaskConf]("task_map"), NextLevel: &LevelMessage{ - Depth: 2, - MapDepth: 2, + Depth: 1, + MapDepth: 1, Indexes: []*LevelIndex{ { Index: &Index{ From 0224d8e5d382fe19ead70fcd45853aab3f7e4325 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Wed, 11 Mar 2026 15:16:28 +0800 Subject: [PATCH 04/32] feat: Add Fruit5Conf support with indexing at the country level - Implemented the Fruit5Conf message structure with a 3-level map (fruit_map -> country_map -> item_map). - Added indexing functionality for the country level, allowing retrieval of countries by name. - Updated generator logic to handle the new Fruit5Conf structure without generating extra LevelIndex key structs. - Enhanced tests to validate the behavior of the new Fruit5Conf, ensuring correct index generation and retrieval. - Updated Go and C++ loader implementations to support the new Fruit5Conf message. - Added a minimal JSON configuration file for Fruit5Conf. --- .../indexes/generator.go | 16 +- .../indexes/index.go | 56 +++--- .../indexes/ordered_index.go | 56 +++--- .../indexes/generator.go | 16 +- .../indexes/index.go | 72 +++---- .../indexes/ordered_index.go | 70 +++---- internal/index/descriptor_test.go | 97 ++++++++++ .../cpp-tableau-loader/src/protoconf/hub.pc.h | 5 + .../src/protoconf/hub_shard0.pc.cc | 7 + .../src/protoconf/index_conf.pc.cc | 106 ++++++++++ .../src/protoconf/index_conf.pc.h | 46 +++++ .../protoconf/loader/hub.pc.go | 4 + .../protoconf/loader/index_conf.pc.go | 183 ++++++++++++++++++ .../protoconf/loader/messager_container.pc.go | 6 + test/proto/index_conf.proto | 27 +++ test/testdata/conf/Fruit5Conf.json | 1 + 16 files changed, 629 insertions(+), 139 deletions(-) create mode 100644 test/testdata/conf/Fruit5Conf.json diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index e5ac1cf1..3c5ce7b8 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -84,18 +84,22 @@ func (x *Generator) GenHppIndexFinders() { if !x.NeedGenerate() { return } - // maxDepth-2: since maxDepth is the 0-based MapDepth of the deepest index level, and LevelIndex keys - // start from the 2nd map level onward, the number of LevelIndex key types is (maxDepth - 1 - 1) = maxDepth - 2. - for i := 1; i <= x.maxDepth-2 && i <= len(x.mapFds)-1; i++ { - if i == 1 { + // The loop generates LevelIndex key types for upper-level map containers. + // - i < x.maxDepth-2: maxDepth is the 0-based MapDepth of the deepest level that has indexes. + // LevelIndex keys start from the 2nd map level (mapFds[1]) onward, so the count is maxDepth-2. + // This ensures we only generate key types up to the deepest level that actually has indexes. + // - i < len(x.mapFds)-1: defensive bound check to prevent out-of-range access on x.mapFds[i+1]. + // In practice, maxDepth <= len(x.mapFds) always holds, so maxDepth-2 <= len(x.mapFds)-2 < len(x.mapFds)-1. + for i := 0; i < x.maxDepth-2 && i < len(x.mapFds)-1; i++ { + if i == 0 { x.g.P() x.g.P(helper.Indent(1), "// LevelIndex keys.") x.g.P(" public:") } - fd := x.mapFds[i] + fd := x.mapFds[i+1] keyType := x.levelKeyType(fd) x.g.P(helper.Indent(1), "struct ", keyType, " {") - keys := x.keys[:i+1] + keys := x.keys[:i+2] for _, key := range keys { x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") } diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index 5ecfc14a..262b6791 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -104,33 +104,33 @@ func (x *Generator) genHppIndexFinders() { x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - partKeys := x.keys[:i] + partKeys := x.keys[:i+1] x.g.P(helper.Indent(1), "// Finds the index: key(", index.Index, ") to value(", vectorType, "),") - x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i+1), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are represented by a vector.") x.g.P(helper.Indent(1), "const ", mapType, "* Find", index.Name(), "Map(", partKeys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") } x.g.P() x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index, 0), ";") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - if i == 1 { - x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.indexContainerName(index, i), ";") + if i == 0 { + x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.indexContainerName(index, i+1), ";") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) - x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.indexContainerName(index, i), ";") + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.indexContainerName(index, i+1), ";") } } } @@ -146,11 +146,11 @@ func (x *Generator) genIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P(helper.Indent(1), x.indexContainerName(index, 0), ".clear();") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - x.g.P(helper.Indent(1), x.indexContainerName(index, i), ".clear();") + x.g.P(helper.Indent(1), x.indexContainerName(index, i+1), ".clear();") } } } @@ -235,18 +235,18 @@ func (x *Generator) generateOneCppMulticolumnIndex(levelMessage *index.LevelMess func (x *Generator) genLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.indexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - if i == 1 { - x.g.P(helper.Indent(ident), x.indexContainerName(index, i), "[k1][", key, "].push_back(&", parentDataName, ");") + if i == 0 { + x.g.P(helper.Indent(ident), x.indexContainerName(index, i+1), "[k1][", key, "].push_back(&", parentDataName, ");") } else { var fields []string - for j := 1; j <= i; j++ { + for j := 1; j <= i+1; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - x.g.P(helper.Indent(ident), x.indexContainerName(index, i), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") + x.g.P(helper.Indent(ident), x.indexContainerName(index, i+1), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") } } } @@ -277,11 +277,11 @@ func (x *Generator) genIndexSorter() { x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(1), "}") // Iterate all leveled containers. - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index, i), ") {") + x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index, i+1), ") {") x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(2), "}") @@ -332,12 +332,12 @@ func (x *Generator) genCppIndexFinders() { x.g.P("}") x.g.P() - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - indexContainerName := x.indexContainerName(index, i) - partKeys := x.keys[:i] + indexContainerName := x.indexContainerName(index, i+1) + partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() partArgs := partKeys.GenGetArguments() x.g.P("const ", messagerName, "::", mapType, "* ", messagerName, "::Find", index.Name(), "Map(", partParams, ") const {") diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index a2f35e48..878d7ecc 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -94,33 +94,33 @@ func (x *Generator) genHppOrderedIndexFinders() { x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - partKeys := x.keys[:i] + partKeys := x.keys[:i+1] x.g.P(helper.Indent(1), "// Finds the ordered index: key(", index.Index, ") to value(", vectorType, "),") - x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are represented by a vector.") x.g.P(helper.Indent(1), "const ", mapType, "* Find", index.Name(), "Map(", partKeys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") } x.g.P() x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.orderedIndexContainerName(index, 0), ";") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - if i == 1 { - x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.orderedIndexContainerName(index, i), ";") + if i == 0 { + x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.orderedIndexContainerName(index, i+1), ";") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) - x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.orderedIndexContainerName(index, i), ";") + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.orderedIndexContainerName(index, i+1), ";") } } } @@ -136,11 +136,11 @@ func (x *Generator) genOrderedIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, 0), ".clear();") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, i), ".clear();") + x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, i+1), ".clear();") } } } @@ -225,18 +225,18 @@ func (x *Generator) generateOneCppMulticolumnOrderedIndex(levelMessage *index.Le func (x *Generator) genOrderedLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - if i == 1 { - x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i), "[k1][", key, "].push_back(&", parentDataName, ");") + if i == 0 { + x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i+1), "[k1][", key, "].push_back(&", parentDataName, ");") } else { var fields []string - for j := 1; j <= i; j++ { + for j := 1; j <= i+1; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") + x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i+1), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") } } } @@ -267,11 +267,11 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(1), "}") // Iterate all leveled containers. - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - x.g.P(helper.Indent(1), "for (auto&& item : ", x.orderedIndexContainerName(index, i), ") {") + x.g.P(helper.Indent(1), "for (auto&& item : ", x.orderedIndexContainerName(index, i+1), ") {") x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(2), "}") @@ -322,12 +322,12 @@ func (x *Generator) genCppOrderedIndexFinders() { x.g.P("}") x.g.P() - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - indexContainerNameI := x.orderedIndexContainerName(index, i) - partKeys := x.keys[:i] + indexContainerNameI := x.orderedIndexContainerName(index, i+1) + partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() partArgs := partKeys.GenGetArguments() x.g.P("const ", messagerName, "::", mapType, "* ", messagerName, "::Find", index.Name(), "Map(", partParams, ") const {") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index 2cbd84c5..635f5a5e 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -87,16 +87,20 @@ func (x *Generator) GenIndexTypeDef() { if !x.NeedGenerate() { return } - // maxDepth-2: since maxDepth is the 0-based MapDepth of the deepest index level, and LevelIndex keys - // start from the 2nd map level onward, the number of LevelIndex key types is (maxDepth - 1 - 1) = maxDepth - 2. - for i := 1; i <= x.maxDepth-2 && i <= len(x.mapFds)-1; i++ { - if i == 1 { + // The loop generates LevelIndex key types for upper-level map containers. + // - i < x.maxDepth-2: maxDepth is the 0-based MapDepth of the deepest level that has indexes. + // LevelIndex keys start from the 2nd map level (mapFds[1]) onward, so the count is maxDepth-2. + // This ensures we only generate key types up to the deepest level that actually has indexes. + // - i < len(x.mapFds)-1: defensive bound check to prevent out-of-range access on x.mapFds[i+1]. + // In practice, maxDepth <= len(x.mapFds) always holds, so maxDepth-2 <= len(x.mapFds)-2 < len(x.mapFds)-1. + for i := 0; i < x.maxDepth-2 && i < len(x.mapFds)-1; i++ { + if i == 0 { x.g.P() x.g.P("// LevelIndex keys.") } - fd := x.mapFds[i] + fd := x.mapFds[i+1] keyType := x.levelKeyType(fd) - keys := x.keys[:i+1] + keys := x.keys[:i+2] x.g.P("type ", keyType, " struct {") for _, key := range keys { x.g.P(key.FieldName, " ", key.Type) diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index 39746b83..7c395fd9 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -83,15 +83,15 @@ func (x *Generator) genIndexField() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P(x.indexContainerName(index, 0), " ", x.indexMapType(index)) - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - if i == 1 { - x.g.P(x.indexContainerName(index, i), " map[", x.keys[0].Type, "]", x.indexMapType(index)) + if i == 0 { + x.g.P(x.indexContainerName(index, i+1), " map[", x.keys[0].Type, "]", x.indexMapType(index)) } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) - x.g.P(x.indexContainerName(index, i), " map[", levelIndexKeyType, "]", x.indexMapType(index)) + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + x.g.P(x.indexContainerName(index, i+1), " map[", levelIndexKeyType, "]", x.indexMapType(index)) } } } @@ -107,15 +107,15 @@ func (x *Generator) genIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P("x.", x.indexContainerName(index, 0), " = make(", x.indexMapType(index), ")") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - if i == 1 { - x.g.P("x.", x.indexContainerName(index, i), " = make(map[", x.keys[0].Type, "]", x.indexMapType(index), ")") + if i == 0 { + x.g.P("x.", x.indexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]", x.indexMapType(index), ")") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) - x.g.P("x.", x.indexContainerName(index, i), " = make(map[", levelIndexKeyType, "]", x.indexMapType(index), ")") + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + x.g.P("x.", x.indexContainerName(index, i+1), " = make(map[", levelIndexKeyType, "]", x.indexMapType(index), ")") } } } @@ -197,22 +197,22 @@ func (x *Generator) generateOneMulticolumnIndex(levelMessage *index.LevelMessage func (x *Generator) genIndexLoaderCommon(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { indexContainerName := x.indexContainerName(index, 0) x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - indexContainerName := x.indexContainerName(index, i) - if i == 1 { + indexContainerName := x.indexContainerName(index, i+1) + if i == 0 { x.g.P("if x.", indexContainerName, "[k1] == nil {") x.g.P("x.", indexContainerName, "[k1] = make(", x.indexMapType(index), ")") x.g.P("}") x.g.P("x.", indexContainerName, "[k1][key] = append(x.", indexContainerName, "[k1][key], ", parentDataName, ")") } else { var fields []string - for j := 1; j <= i; j++ { + for j := 1; j <= i+1; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) keyName := indexContainerName + "Keys" x.g.P(keyName, " := ", levelIndexKeyType, "{", strings.Join(fields, ", "), "}") x.g.P("if x.", indexContainerName, "[", keyName, "] == nil {") @@ -247,11 +247,11 @@ func (x *Generator) genIndexSorter() { x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("}") // Iterate all leveled containers. - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - x.g.P("for _, itemMap := range x.", x.indexContainerName(index, i), " {") + x.g.P("for _, itemMap := range x.", x.indexContainerName(index, i+1), " {") x.g.P("for _, itemList := range itemMap {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("}") @@ -304,43 +304,43 @@ func (x *Generator) genIndexFinders() { x.g.P("}") x.g.P() - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - indexContainerName := x.indexContainerName(index, i) - partKeys := x.keys[:i] + indexContainerName := x.indexContainerName(index, i+1) + partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() partArgs := partKeys.GenGetArguments() - x.g.P("// Find", index.Name(), "Map", i, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") - x.g.P("// which is the upper ", loadutil.Ordinal(i), "-level map specified by (", partArgs, ").") + x.g.P("// Find", index.Name(), "Map", i+1, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") + x.g.P("// which is the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partArgs, ").") x.g.P("// One key may correspond to multiple values, which are represented by a slice.") - x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i, "(", partParams, ") ", x.indexMapType(index), " {") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i+1, "(", partParams, ") ", x.indexMapType(index), " {") if len(partKeys) == 1 { x.g.P("return x.", indexContainerName, "[", partArgs, "]") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) x.g.P("return x.", indexContainerName, "[", levelIndexKeyType, "{", partArgs, "}]") } x.g.P("}") x.g.P() - x.g.P("// Find", index.Name(), i, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map") + x.g.P("// Find", index.Name(), i+1, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level map") x.g.P("// specified by (", partArgs, ").") - x.g.P("func (x *", messagerName, ") Find", index.Name(), i, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") + x.g.P("func (x *", messagerName, ") Find", index.Name(), i+1, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") if len(index.ColFields) == 1 { - x.g.P("return x.Find", index.Name(), "Map", i, "(", partArgs, ")[", args, "]") + x.g.P("return x.Find", index.Name(), "Map", i+1, "(", partArgs, ")[", args, "]") } else { - x.g.P("return x.Find", index.Name(), "Map", i, "(", partArgs, ")[", x.indexMapKeyType(index), "{", args, "}]") + x.g.P("return x.Find", index.Name(), "Map", i+1, "(", partArgs, ")[", x.indexMapKeyType(index), "{", args, "}]") } x.g.P("}") x.g.P() - x.g.P("// FindFirst", index.Name(), i, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map") + x.g.P("// FindFirst", index.Name(), i+1, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level map") x.g.P("// specified by (", partArgs, "), or nil if no value found.") - x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") - x.g.P("val := x.Find", index.Name(), i, "(", partArgs, ", ", args, ")") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i+1, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), i+1, "(", partArgs, ", ", args, ")") x.g.P("if len(val) > 0 {") x.g.P("return val[0]") x.g.P("}") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go index f2e2196d..0c111ff9 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go @@ -107,15 +107,15 @@ func (x *Generator) genOrderedIndexField() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P(x.orderedIndexContainerName(index, 0), " *", x.orderedIndexMapType(index)) - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - if i == 1 { - x.g.P(x.orderedIndexContainerName(index, i), " map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index)) + if i == 0 { + x.g.P(x.orderedIndexContainerName(index, i+1), " map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index)) } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) - x.g.P(x.orderedIndexContainerName(index, i), " map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index)) + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + x.g.P(x.orderedIndexContainerName(index, i+1), " map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index)) } } } @@ -131,15 +131,15 @@ func (x *Generator) genOrderedIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P("x.", x.orderedIndexContainerName(index, 0), " = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - if i == 1 { - x.g.P("x.", x.orderedIndexContainerName(index, i), " = make(map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index), ")") + if i == 0 { + x.g.P("x.", x.orderedIndexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index), ")") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) - x.g.P("x.", x.orderedIndexContainerName(index, i), " = make(map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index), ")") + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + x.g.P("x.", x.orderedIndexContainerName(index, i+1), " = make(map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index), ")") } } } @@ -223,13 +223,13 @@ func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage indexContainerName := x.orderedIndexContainerName(index, 0) x.g.P("value, _ := x.", indexContainerName, ".Get(key)") x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - orderedIndexContainerName := x.orderedIndexContainerName(index, i) + orderedIndexContainerName := x.orderedIndexContainerName(index, i+1) valueName := orderedIndexContainerName + "Value" - if i == 1 { + if i == 0 { x.g.P("if x.", orderedIndexContainerName, "[k1] == nil {") x.g.P("x.", orderedIndexContainerName, "[k1] = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") x.g.P("}") @@ -237,10 +237,10 @@ func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage x.g.P("x.", orderedIndexContainerName, "[k1].Put(key, append(", valueName, ", ", parentDataName, "))") } else { var fields []string - for j := 1; j <= i; j++ { + for j := 1; j <= i+1; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) keyName := orderedIndexContainerName + "Keys" x.g.P(keyName, " := ", levelIndexKeyType, "{", strings.Join(fields, ", "), "}") x.g.P("if x.", orderedIndexContainerName, "[", keyName, "] == nil {") @@ -277,11 +277,11 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P("return true") x.g.P("})") // Iterate all leveled containers. - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - x.g.P("for _, itemMap := range x.", x.orderedIndexContainerName(index, i), " {") + x.g.P("for _, itemMap := range x.", x.orderedIndexContainerName(index, i+1), " {") x.g.P("itemMap.Range(func(key ", x.orderedIndexMapKeyType(index), ", itemList []*", x.mapValueType(index), ") bool {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("return true") @@ -336,32 +336,32 @@ func (x *Generator) genOrderedIndexFinders() { x.g.P("}") x.g.P() - for i := 1; i <= levelMessage.NumLeveledContainers(); i++ { - if i > len(x.keys) { + for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + if i >= len(x.keys) { break } - orderedIndexContainerName := x.orderedIndexContainerName(index, i) - partKeys := x.keys[:i] + orderedIndexContainerName := x.orderedIndexContainerName(index, i+1) + partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() partArgs := partKeys.GenGetArguments() - x.g.P("// Find", index.Name(), "Map", i, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") - x.g.P("// which is the upper ", loadutil.Ordinal(i), "-level treemap specified by (", partArgs, ").") + x.g.P("// Find", index.Name(), "Map", i+1, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") + x.g.P("// which is the upper ", loadutil.Ordinal(i+1), "-level treemap specified by (", partArgs, ").") x.g.P("// One key may correspond to multiple values, which are represented by a slice.") - x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i, "(", partParams, ") *", x.orderedIndexMapType(index), " {") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i+1, "(", partParams, ") *", x.orderedIndexMapType(index), " {") if len(partKeys) == 1 { x.g.P("return x.", orderedIndexContainerName, "[", partArgs, "]") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i-1]) + levelIndexKeyType := x.levelKeyType(x.mapFds[i]) x.g.P("return x.", orderedIndexContainerName, "[", levelIndexKeyType, "{", partArgs, "}]") } x.g.P("}") x.g.P() - x.g.P("// Find", index.Name(), i, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level treemap") + x.g.P("// Find", index.Name(), i+1, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level treemap") x.g.P("// specified by (", partArgs, ").") - x.g.P("func (x *", messagerName, ") Find", index.Name(), i, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") - x.g.P("m := x.Find", index.Name(), "Map", i, "(", partArgs, ")") + x.g.P("func (x *", messagerName, ") Find", index.Name(), i+1, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") + x.g.P("m := x.Find", index.Name(), "Map", i+1, "(", partArgs, ")") x.g.P("if m == nil {") x.g.P("return nil") x.g.P("}") @@ -374,10 +374,10 @@ func (x *Generator) genOrderedIndexFinders() { x.g.P("}") x.g.P() - x.g.P("// FindFirst", index.Name(), i, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level treemap") + x.g.P("// FindFirst", index.Name(), i+1, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level treemap") x.g.P("// specified by (", partArgs, "), or nil if no value found.") - x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") - x.g.P("val := x.Find", index.Name(), i, "(", partArgs, ", ", args, ")") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i+1, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), i+1, "(", partArgs, ", ", args, ")") x.g.P("if len(val) > 0 {") x.g.P("return val[0]") x.g.P("}") diff --git a/internal/index/descriptor_test.go b/internal/index/descriptor_test.go index e359189d..7904aa3f 100644 --- a/internal/index/descriptor_test.go +++ b/internal/index/descriptor_test.go @@ -589,6 +589,55 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, }, + { + // Fruit5Conf: 3-level map (fruit_map -> country_map -> item_map), + // but indexes only at MapDepth=2 (Country level). + // The 3rd level map (MapDepth=3, Item) has no index. + // This validates that maxDepth (=2) < len(mapFds) (=3) does not cause + // generation of extra LevelIndex key structs. + name: "Fruit5Conf", + args: args{ + md: md[*protoconf.Fruit5Conf](), + }, + want: &IndexDescriptor{ + LevelMessage: &LevelMessage{ + Depth: 0, + MapDepth: 0, + FD: fd[*protoconf.Fruit5Conf]("fruit_map"), + NextLevel: &LevelMessage{ + Depth: 1, + MapDepth: 1, + FD: fd[*protoconf.Fruit5Conf_Fruit]("country_map"), + NextLevel: &LevelMessage{ + Depth: 2, + MapDepth: 2, + FD: fd[*protoconf.Fruit5Conf_Fruit_Country]("item_map"), + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"CountryName"}, + Name: "", + }, + MD: md[*protoconf.Fruit5Conf_Fruit_Country](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Fruit5Conf_Fruit_Country]("name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Fruit5Conf_Fruit_Country]("name"), + }, + }, + }, + }, + }, + NextLevel: &LevelMessage{ + Depth: 3, + MapDepth: 3, + }, + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -597,3 +646,51 @@ func Test_ParseIndexDescriptor(t *testing.T) { }) } } + +// Test_Fruit5Conf_MaxDepthVsMapFds verifies that when the deepest map level +// (MapDepth=3) has no index but a shallower level (MapDepth=2) does, +// the generator loop `for i := 0; i < maxDepth-2 && i < len(mapFds)-1; i++` +// does NOT produce extra LevelIndex key structs. +// +// Fruit5Conf has 3-level map (fruit_map -> country_map -> item_map) but +// indexes only at MapDepth=2 (Country), so maxDepth=2, len(mapFds)=3. +// Without the maxDepth-2 guard, the loop would run 2 iterations instead of 0. +func Test_Fruit5Conf_MaxDepthVsMapFds(t *testing.T) { + descriptor := ParseIndexDescriptor(md[*protoconf.Fruit5Conf]()) + + // Simulate initLevelMessage logic to collect maxDepth and mapFds count. + var maxDepth int + var mapFdCount int + for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + if fd := levelMessage.FD; fd != nil && fd.IsMap() { + mapFdCount++ + } + if len(levelMessage.Indexes) != 0 || len(levelMessage.OrderedIndexes) != 0 { + maxDepth = levelMessage.MapDepth + } + } + + // Fruit5Conf: 3 map levels, but deepest index is at MapDepth=2. + assert.Equal(t, 3, mapFdCount, "Fruit5Conf should have 3 map field descriptors") + assert.Equal(t, 2, maxDepth, "Fruit5Conf's deepest index should be at MapDepth=2") + assert.Less(t, maxDepth, mapFdCount, "maxDepth should be less than len(mapFds)") + + // Verify the LevelIndex key generation loop would produce 0 iterations, + // meaning no extra LevelIndex key structs are generated. + levelIndexKeyCount := 0 + for i := 0; i < maxDepth-2 && i < mapFdCount-1; i++ { + levelIndexKeyCount++ + } + assert.Equal(t, 0, levelIndexKeyCount, + "No LevelIndex key structs should be generated: maxDepth-2=%d, len(mapFds)-1=%d", + maxDepth-2, mapFdCount-1) + + // Contrast: without the maxDepth-2 guard, using only len(mapFds)-1 + // would incorrectly generate 2 LevelIndex key structs. + wrongKeyCount := 0 + for i := 0; i < mapFdCount-1; i++ { + wrongKeyCount++ + } + assert.Equal(t, 2, wrongKeyCount, + "Without maxDepth guard, %d extra LevelIndex key structs would be generated", wrongKeyCount) +} diff --git a/test/cpp-tableau-loader/src/protoconf/hub.pc.h b/test/cpp-tableau-loader/src/protoconf/hub.pc.h index cd7c8d69..14f7ca08 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/hub.pc.h @@ -138,6 +138,10 @@ class Fruit4Conf; template <> const std::shared_ptr Hub::Get() const; +class Fruit5Conf; +template <> +const std::shared_ptr Hub::Get() const; + class FruitConf; template <> const std::shared_ptr Hub::Get() const; @@ -193,6 +197,7 @@ class MessagerContainer { std::shared_ptr fruit_2_conf_; std::shared_ptr fruit_3_conf_; std::shared_ptr fruit_4_conf_; + std::shared_ptr fruit_5_conf_; std::shared_ptr fruit_conf_; std::shared_ptr item_conf_; std::shared_ptr patch_merge_conf_; diff --git a/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc b/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc index d82400ff..16ec170f 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc @@ -35,6 +35,11 @@ const std::shared_ptr Hub::Get() const { return GetMessagerContainerWithProvider()->fruit_4_conf_; } +template <> +const std::shared_ptr Hub::Get() const { + return GetMessagerContainerWithProvider()->fruit_5_conf_; +} + template <> const std::shared_ptr Hub::Get() const { return GetMessagerContainerWithProvider()->fruit_conf_; @@ -51,6 +56,7 @@ void MessagerContainer::InitShard0() { fruit_2_conf_ = std::dynamic_pointer_cast(GetMessager(Fruit2Conf::Name())); fruit_3_conf_ = std::dynamic_pointer_cast(GetMessager(Fruit3Conf::Name())); fruit_4_conf_ = std::dynamic_pointer_cast(GetMessager(Fruit4Conf::Name())); + fruit_5_conf_ = std::dynamic_pointer_cast(GetMessager(Fruit5Conf::Name())); fruit_conf_ = std::dynamic_pointer_cast(GetMessager(FruitConf::Name())); item_conf_ = std::dynamic_pointer_cast(GetMessager(ItemConf::Name())); } @@ -61,6 +67,7 @@ void Registry::InitShard0() { Register(); Register(); Register(); + Register(); Register(); Register(); } diff --git a/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc index 3ea0cf8f..00527afc 100644 --- a/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.cc @@ -718,4 +718,110 @@ const protoconf::Fruit4Conf::Fruit::Country::Item* Fruit4Conf::FindFirstItem(int return conf->front(); } +const std::string Fruit5Conf::kProtoName = protoconf::Fruit5Conf::GetDescriptor()->name(); + +bool Fruit5Conf::Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options /* = nullptr */) { + tableau::util::TimeProfiler profiler; + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); + bool ok = loaded ? ProcessAfterLoad() : false; + stats_.duration = profiler.Elapse(); + return ok; +} + +bool Fruit5Conf::ProcessAfterLoad() { + // Index init. + index_country_map_.clear(); + index_country_map1_.clear(); + for (auto&& item1 : data_.fruit_map()) { + auto k1 = item1.first; + for (auto&& item2 : item1.second.country_map()) { + { + // Index: CountryName + index_country_map_[item2.second.name()].push_back(&item2.second); + index_country_map1_[k1][item2.second.name()].push_back(&item2.second); + } + } + } + return true; +} + +const protoconf::Fruit5Conf::Fruit* Fruit5Conf::Get(int32_t fruit_type) const { + auto iter = data_.fruit_map().find(fruit_type); + if (iter == data_.fruit_map().end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit5Conf::Fruit::Country* Fruit5Conf::Get(int32_t fruit_type, int32_t id) const { + const auto* conf = Get(fruit_type); + if (conf == nullptr) { + return nullptr; + } + auto iter = conf->country_map().find(id); + if (iter == conf->country_map().end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit5Conf::Fruit::Country::Item* Fruit5Conf::Get(int32_t fruit_type, int32_t id, int32_t id3) const { + const auto* conf = Get(fruit_type, id); + if (conf == nullptr) { + return nullptr; + } + auto iter = conf->item_map().find(id3); + if (iter == conf->item_map().end()) { + return nullptr; + } + return &iter->second; +} + +// Index: CountryName +const Fruit5Conf::Index_CountryMap& Fruit5Conf::FindCountryMap() const { return index_country_map_; } + +const Fruit5Conf::Index_CountryVector* Fruit5Conf::FindCountry(const std::string& name) const { + auto iter = index_country_map_.find(name); + if (iter == index_country_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit5Conf::Fruit::Country* Fruit5Conf::FindFirstCountry(const std::string& name) const { + auto conf = FindCountry(name); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +const Fruit5Conf::Index_CountryMap* Fruit5Conf::FindCountryMap(int32_t fruit_type) const { + auto iter = index_country_map1_.find(fruit_type); + if (iter == index_country_map1_.end()) { + return nullptr; + } + return &iter->second; +} + +const Fruit5Conf::Index_CountryVector* Fruit5Conf::FindCountry(int32_t fruit_type, const std::string& name) const { + auto map = FindCountryMap(fruit_type); + if (map == nullptr) { + return nullptr; + } + auto iter = map->find(name); + if (iter == map->end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::Fruit5Conf::Fruit::Country* Fruit5Conf::FindFirstCountry(int32_t fruit_type, const std::string& name) const { + auto conf = FindCountry(fruit_type, name); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + } // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/index_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.h index ac4040ae..76e6431e 100644 --- a/test/cpp-tableau-loader/src/protoconf/index_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/index_conf.pc.h @@ -334,6 +334,51 @@ class Fruit4Conf final : public Messager { std::unordered_map ordered_index_item_map2_; }; +class Fruit5Conf final : public Messager { + public: + static const std::string& Name() { return kProtoName; } + virtual bool Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options = nullptr) override; + const protoconf::Fruit5Conf& Data() const { return data_; } + const google::protobuf::Message* Message() const override { return &data_; } + + private: + virtual bool ProcessAfterLoad() override; + + public: + const protoconf::Fruit5Conf::Fruit* Get(int32_t fruit_type) const; + const protoconf::Fruit5Conf::Fruit::Country* Get(int32_t fruit_type, int32_t id) const; + const protoconf::Fruit5Conf::Fruit::Country::Item* Get(int32_t fruit_type, int32_t id, int32_t id3) const; + + private: + static const std::string kProtoName; + protoconf::Fruit5Conf data_; + + // Index accessers. + // Index: CountryName + public: + using Index_CountryVector = std::vector; + using Index_CountryMap = std::unordered_map; + // Finds the index: key(CountryName) to value(Index_CountryVector) hashmap. + // One key may correspond to multiple values, which are represented by a vector. + const Index_CountryMap& FindCountryMap() const; + // Finds a vector of all values of the given key(s). + const Index_CountryVector* FindCountry(const std::string& name) const; + // Finds the first value of the given key(s). + const protoconf::Fruit5Conf::Fruit::Country* FindFirstCountry(const std::string& name) const; + // Finds the index: key(CountryName) to value(Index_CountryVector), + // which is the upper 1st-level hashmap specified by (fruit_type). + // One key may correspond to multiple values, which are represented by a vector. + const Index_CountryMap* FindCountryMap(int32_t fruit_type) const; + // Finds a vector of all values of the given key(s) in the upper 1st-level hashmap specified by (fruit_type). + const Index_CountryVector* FindCountry(int32_t fruit_type, const std::string& name) const; + // Finds the first value of the given key(s) in the upper 1st-level hashmap specified by (fruit_type). + const protoconf::Fruit5Conf::Fruit::Country* FindFirstCountry(int32_t fruit_type, const std::string& name) const; + + private: + Index_CountryMap index_country_map_; + std::unordered_map index_country_map1_; +}; + } // namespace tableau namespace protoconf { @@ -342,4 +387,5 @@ using FruitConfMgr = tableau::FruitConf; using Fruit2ConfMgr = tableau::Fruit2Conf; using Fruit3ConfMgr = tableau::Fruit3Conf; using Fruit4ConfMgr = tableau::Fruit4Conf; +using Fruit5ConfMgr = tableau::Fruit5Conf; } // namespace protoconf diff --git a/test/go-tableau-loader/protoconf/loader/hub.pc.go b/test/go-tableau-loader/protoconf/loader/hub.pc.go index ca63f9be..d34babe8 100644 --- a/test/go-tableau-loader/protoconf/loader/hub.pc.go +++ b/test/go-tableau-loader/protoconf/loader/hub.pc.go @@ -235,6 +235,10 @@ func (h *Hub) GetFruit4Conf() *Fruit4Conf { return h.mc.Load().GetFruit4Conf() } +func (h *Hub) GetFruit5Conf() *Fruit5Conf { + return h.mc.Load().GetFruit5Conf() +} + func (h *Hub) GetItemConf() *ItemConf { return h.mc.Load().GetItemConf() } diff --git a/test/go-tableau-loader/protoconf/loader/index_conf.pc.go b/test/go-tableau-loader/protoconf/loader/index_conf.pc.go index 1fc0ad56..0ea56050 100644 --- a/test/go-tableau-loader/protoconf/loader/index_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/index_conf.pc.go @@ -1106,6 +1106,186 @@ func (x *Fruit4Conf) FindFirstItem2(fruitType int32, id int32, price int32) *pro return nil } +// Index types. +// Index: CountryName +type Fruit5Conf_Index_CountryMap = map[string][]*protoconf.Fruit5Conf_Fruit_Country + +// Fruit5Conf is a wrapper around protobuf message: protoconf.Fruit5Conf. +// +// It is designed for three goals: +// +// 1. Easy use: simple yet powerful accessers. +// 2. Elegant API: concise and clean functions. +// 3. Extensibility: Map, OrdererdMap, Index, OrderedIndex... +type Fruit5Conf struct { + UnimplementedMessager + data, originalData *protoconf.Fruit5Conf + indexCountryMap Fruit5Conf_Index_CountryMap + indexCountryMap1 map[int32]Fruit5Conf_Index_CountryMap +} + +// Name returns the Fruit5Conf's message name. +func (x *Fruit5Conf) Name() string { + return string((*protoconf.Fruit5Conf)(nil).ProtoReflect().Descriptor().Name()) +} + +// Data returns the Fruit5Conf's inner message data. +func (x *Fruit5Conf) Data() *protoconf.Fruit5Conf { + if x != nil { + return x.data + } + return nil +} + +// Load loads Fruit5Conf's content in the given dir, based on format and messager options. +func (x *Fruit5Conf) Load(dir string, format format.Format, opts *load.MessagerOptions) error { + start := time.Now() + defer func() { + x.Stats.Duration = time.Since(start) + }() + x.data = &protoconf.Fruit5Conf{} + err := load.LoadMessagerInDir(x.data, dir, format, opts) + if err != nil { + return err + } + if x.backup { + x.originalData = proto.Clone(x.data).(*protoconf.Fruit5Conf) + } + return x.processAfterLoad() +} + +// Store stores Fruit5Conf's content to file in the specified directory and format. +// Available formats: JSON, Bin, and Text. +func (x *Fruit5Conf) Store(dir string, format format.Format, options ...store.Option) error { + return store.Store(x.Data(), dir, format, options...) +} + +// Message returns the Fruit5Conf's inner message data. +func (x *Fruit5Conf) Message() proto.Message { + return x.Data() +} + +// Messager returns the current messager. +func (x *Fruit5Conf) Messager() Messager { + return x +} + +// originalMessage returns the Fruit5Conf's original inner message. +func (x *Fruit5Conf) originalMessage() proto.Message { + if x != nil { + return x.originalData + } + return nil +} + +// processAfterLoad runs after this messager is loaded. +func (x *Fruit5Conf) processAfterLoad() error { + // Index init. + x.indexCountryMap = make(Fruit5Conf_Index_CountryMap) + x.indexCountryMap1 = make(map[int32]Fruit5Conf_Index_CountryMap) + for k1, v1 := range x.data.GetFruitMap() { + for _, v2 := range v1.GetCountryMap() { + { + // Index: CountryName + key := v2.GetName() + x.indexCountryMap[key] = append(x.indexCountryMap[key], v2) + if x.indexCountryMap1[k1] == nil { + x.indexCountryMap1[k1] = make(Fruit5Conf_Index_CountryMap) + } + x.indexCountryMap1[k1][key] = append(x.indexCountryMap1[k1][key], v2) + } + } + } + return nil +} + +// Get1 finds value in the 1st-level map. It will return +// NotFound error if the key is not found. +func (x *Fruit5Conf) Get1(fruitType int32) (*protoconf.Fruit5Conf_Fruit, error) { + d := x.Data().GetFruitMap() + if val, ok := d[fruitType]; !ok { + return nil, fmt.Errorf("fruitType(%v) %w", fruitType, ErrNotFound) + } else { + return val, nil + } +} + +// Get2 finds value in the 2nd-level map. It will return +// NotFound error if the key is not found. +func (x *Fruit5Conf) Get2(fruitType int32, id int32) (*protoconf.Fruit5Conf_Fruit_Country, error) { + conf, err := x.Get1(fruitType) + if err != nil { + return nil, err + } + d := conf.GetCountryMap() + if val, ok := d[id]; !ok { + return nil, fmt.Errorf("id(%v) %w", id, ErrNotFound) + } else { + return val, nil + } +} + +// Get3 finds value in the 3rd-level map. It will return +// NotFound error if the key is not found. +func (x *Fruit5Conf) Get3(fruitType int32, id int32, id3 int32) (*protoconf.Fruit5Conf_Fruit_Country_Item, error) { + conf, err := x.Get2(fruitType, id) + if err != nil { + return nil, err + } + d := conf.GetItemMap() + if val, ok := d[id3]; !ok { + return nil, fmt.Errorf("id3(%v) %w", id3, ErrNotFound) + } else { + return val, nil + } +} + +// Index: CountryName + +// FindCountryMap finds the index: key(CountryName) to value(protoconf.Fruit5Conf_Fruit_Country) map. +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit5Conf) FindCountryMap() Fruit5Conf_Index_CountryMap { + return x.indexCountryMap +} + +// FindCountry finds a slice of all values of the given key(s). +func (x *Fruit5Conf) FindCountry(name string) []*protoconf.Fruit5Conf_Fruit_Country { + return x.indexCountryMap[name] +} + +// FindFirstCountry finds the first value of the given key(s), +// or nil if no value found. +func (x *Fruit5Conf) FindFirstCountry(name string) *protoconf.Fruit5Conf_Fruit_Country { + val := x.FindCountry(name) + if len(val) > 0 { + return val[0] + } + return nil +} + +// FindCountryMap1 finds the index: key(CountryName) to value(protoconf.Fruit5Conf_Fruit_Country), +// which is the upper 1st-level map specified by (fruitType). +// One key may correspond to multiple values, which are represented by a slice. +func (x *Fruit5Conf) FindCountryMap1(fruitType int32) Fruit5Conf_Index_CountryMap { + return x.indexCountryMap1[fruitType] +} + +// FindCountry1 finds a slice of all values of the given key(s) in the upper 1st-level map +// specified by (fruitType). +func (x *Fruit5Conf) FindCountry1(fruitType int32, name string) []*protoconf.Fruit5Conf_Fruit_Country { + return x.FindCountryMap1(fruitType)[name] +} + +// FindFirstCountry1 finds the first value of the given key(s) in the upper 1st-level map +// specified by (fruitType), or nil if no value found. +func (x *Fruit5Conf) FindFirstCountry1(fruitType int32, name string) *protoconf.Fruit5Conf_Fruit_Country { + val := x.FindCountry1(fruitType, name) + if len(val) > 0 { + return val[0] + } + return nil +} + func init() { Register(func() Messager { return new(FruitConf) @@ -1119,4 +1299,7 @@ func init() { Register(func() Messager { return new(Fruit4Conf) }) + Register(func() Messager { + return new(Fruit5Conf) + }) } diff --git a/test/go-tableau-loader/protoconf/loader/messager_container.pc.go b/test/go-tableau-loader/protoconf/loader/messager_container.pc.go index e483a662..bb7fc8b5 100644 --- a/test/go-tableau-loader/protoconf/loader/messager_container.pc.go +++ b/test/go-tableau-loader/protoconf/loader/messager_container.pc.go @@ -19,6 +19,7 @@ type MessagerContainer struct { fruit2Conf *Fruit2Conf fruit3Conf *Fruit3Conf fruit4Conf *Fruit4Conf + fruit5Conf *Fruit5Conf itemConf *ItemConf patchReplaceConf *PatchReplaceConf patchMergeConf *PatchMergeConf @@ -39,6 +40,7 @@ func newMessagerContainer(messagerMap MessagerMap) *MessagerContainer { fruit2Conf: GetMessager[*Fruit2Conf](messagerMap), fruit3Conf: GetMessager[*Fruit3Conf](messagerMap), fruit4Conf: GetMessager[*Fruit4Conf](messagerMap), + fruit5Conf: GetMessager[*Fruit5Conf](messagerMap), itemConf: GetMessager[*ItemConf](messagerMap), patchReplaceConf: GetMessager[*PatchReplaceConf](messagerMap), patchMergeConf: GetMessager[*PatchMergeConf](messagerMap), @@ -88,6 +90,10 @@ func (mc *MessagerContainer) GetFruit4Conf() *Fruit4Conf { return mc.fruit4Conf } +func (mc *MessagerContainer) GetFruit5Conf() *Fruit5Conf { + return mc.fruit5Conf +} + func (mc *MessagerContainer) GetItemConf() *ItemConf { return mc.itemConf } diff --git a/test/proto/index_conf.proto b/test/proto/index_conf.proto index 2ac16a4a..b6f45c16 100644 --- a/test/proto/index_conf.proto +++ b/test/proto/index_conf.proto @@ -116,4 +116,31 @@ message Fruit4Conf { } } } +} + +// Nesting: map -> map -> map +// 3-level map but indexes only at the 2nd level (MapDepth=2). +// The 3rd level map (MapDepth=3) has no index. +// This tests that maxDepth (=2) < len(mapFds) (=3) does not cause +// generation of extra LevelIndex key structs. +message Fruit5Conf { + option (tableau.worksheet) = { + name: "Fruit5Conf" + index: "CountryName" + }; + + map fruit_map = 1 [(tableau.field) = { key: "FruitType" layout: LAYOUT_VERTICAL }]; + message Fruit { + protoconf.FruitType fruit_type = 1 [(tableau.field) = { name: "FruitType" }]; + map country_map = 2 [(tableau.field) = { name: "Country" key: "ID" layout: LAYOUT_HORIZONTAL }]; + message Country { + int32 id = 1 [(tableau.field) = { name: "ID" }]; + string name = 2 [(tableau.field) = { name: "Name" }]; + map item_map = 3 [(tableau.field) = { name: "Item" key: "ID" layout: LAYOUT_HORIZONTAL }]; + message Item { + int32 id = 1 [(tableau.field) = { name: "ID" }]; + int32 price = 2 [(tableau.field) = { name: "Price" }]; + } + } + } } \ No newline at end of file diff --git a/test/testdata/conf/Fruit5Conf.json b/test/testdata/conf/Fruit5Conf.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/test/testdata/conf/Fruit5Conf.json @@ -0,0 +1 @@ +{} \ No newline at end of file From 3c0e530c4c133241cc7bbb3fd97903ab333e503e Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Wed, 11 Mar 2026 16:43:52 +0800 Subject: [PATCH 05/32] refactor: streamline map key collection logic in generator --- .../indexes/generator.go | 24 ++++---- .../indexes/generator.go | 24 ++++---- internal/index/descriptor.go | 6 ++ internal/index/descriptor_test.go | 61 ++++++++++--------- 4 files changed, 61 insertions(+), 54 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index 3c5ce7b8..c84651a7 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -15,9 +15,8 @@ type Generator struct { message *protogen.Message // level message - maxDepth int - keys helper.MapKeySlice - mapFds []protoreflect.FieldDescriptor + keys helper.MapKeySlice + mapFds []protoreflect.FieldDescriptor } func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { @@ -33,15 +32,17 @@ func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, func (x *Generator) initLevelMessage() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { if fd := levelMessage.FD; fd != nil && fd.IsMap() { + // Only collect map keys/fds when a deeper level has an index or ordered index, + // because these keys are used solely for building upper-level (leveled) containers. + if !levelMessage.NextLevel.NeedGenAnyIndex() { + continue + } x.keys = x.keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), Name: helper.ParseMapFieldName(fd), }) x.mapFds = append(x.mapFds, fd) } - if len(levelMessage.Indexes) != 0 || len(levelMessage.OrderedIndexes) != 0 { - x.maxDepth = levelMessage.MapDepth - } } } @@ -85,12 +86,11 @@ func (x *Generator) GenHppIndexFinders() { return } // The loop generates LevelIndex key types for upper-level map containers. - // - i < x.maxDepth-2: maxDepth is the 0-based MapDepth of the deepest level that has indexes. - // LevelIndex keys start from the 2nd map level (mapFds[1]) onward, so the count is maxDepth-2. - // This ensures we only generate key types up to the deepest level that actually has indexes. - // - i < len(x.mapFds)-1: defensive bound check to prevent out-of-range access on x.mapFds[i+1]. - // In practice, maxDepth <= len(x.mapFds) always holds, so maxDepth-2 <= len(x.mapFds)-2 < len(x.mapFds)-1. - for i := 0; i < x.maxDepth-2 && i < len(x.mapFds)-1; i++ { + // initLevelMessage only collects map keys/fds for levels whose deeper + // levels have indexes, so len(x.mapFds) already reflects the effective + // depth. LevelIndex keys start from the 2nd map level (mapFds[1]) onward, + // so the count is len(x.mapFds)-2. + for i := 0; i < len(x.mapFds)-2; i++ { if i == 0 { x.g.P() x.g.P(helper.Indent(1), "// LevelIndex keys.") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index 635f5a5e..d12178ac 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -16,9 +16,8 @@ type Generator struct { message *protogen.Message // level message - maxDepth int - keys helper.MapKeySlice - mapFds []protoreflect.FieldDescriptor + keys helper.MapKeySlice + mapFds []protoreflect.FieldDescriptor } func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { @@ -35,6 +34,11 @@ func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *i func (x *Generator) initLevelMessage() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { if fd := levelMessage.FD; fd != nil && fd.IsMap() { + // Only collect map keys/fds when a deeper level has an index or ordered index, + // because these keys are used solely for building upper-level (leveled) containers. + if !levelMessage.NextLevel.NeedGenAnyIndex() { + continue + } x.keys = x.keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), Name: helper.ParseMapFieldNameAsFuncParam(fd), @@ -42,9 +46,6 @@ func (x *Generator) initLevelMessage() { }) x.mapFds = append(x.mapFds, fd) } - if len(levelMessage.Indexes) != 0 || len(levelMessage.OrderedIndexes) != 0 { - x.maxDepth = levelMessage.MapDepth - } } } @@ -88,12 +89,11 @@ func (x *Generator) GenIndexTypeDef() { return } // The loop generates LevelIndex key types for upper-level map containers. - // - i < x.maxDepth-2: maxDepth is the 0-based MapDepth of the deepest level that has indexes. - // LevelIndex keys start from the 2nd map level (mapFds[1]) onward, so the count is maxDepth-2. - // This ensures we only generate key types up to the deepest level that actually has indexes. - // - i < len(x.mapFds)-1: defensive bound check to prevent out-of-range access on x.mapFds[i+1]. - // In practice, maxDepth <= len(x.mapFds) always holds, so maxDepth-2 <= len(x.mapFds)-2 < len(x.mapFds)-1. - for i := 0; i < x.maxDepth-2 && i < len(x.mapFds)-1; i++ { + // initLevelMessage only collects map keys/fds for levels whose deeper + // levels have indexes, so len(x.mapFds) already reflects the effective + // depth. LevelIndex keys start from the 2nd map level (mapFds[1]) onward, + // so the count is len(x.mapFds)-2. + for i := 0; i < len(x.mapFds)-2; i++ { if i == 0 { x.g.P() x.g.P("// LevelIndex keys.") diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index 10daef58..af55ace7 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -87,6 +87,12 @@ func (l *LevelMessage) NeedGenOrderedIndex() bool { return len(l.OrderedIndexes) != 0 || l.NextLevel.NeedGenOrderedIndex() } +// NeedGenAnyIndex reports whether this level or any deeper level has +// at least one index (regular or ordered). +func (l *LevelMessage) NeedGenAnyIndex() bool { + return l.NeedGenIndex() || l.NeedGenOrderedIndex() +} + type LevelIndex struct { *Index MD protoreflect.MessageDescriptor diff --git a/internal/index/descriptor_test.go b/internal/index/descriptor_test.go index 7904aa3f..974a6c7a 100644 --- a/internal/index/descriptor_test.go +++ b/internal/index/descriptor_test.go @@ -593,8 +593,9 @@ func Test_ParseIndexDescriptor(t *testing.T) { // Fruit5Conf: 3-level map (fruit_map -> country_map -> item_map), // but indexes only at MapDepth=2 (Country level). // The 3rd level map (MapDepth=3, Item) has no index. - // This validates that maxDepth (=2) < len(mapFds) (=3) does not cause - // generation of extra LevelIndex key structs. + // This validates that initLevelMessage only collects map keys/fds + // up to the deepest level that has indexes, so len(mapFds) == 2 + // (not 3), preventing generation of extra LevelIndex key structs. name: "Fruit5Conf", args: args{ md: md[*protoconf.Fruit5Conf](), @@ -647,50 +648,50 @@ func Test_ParseIndexDescriptor(t *testing.T) { } } -// Test_Fruit5Conf_MaxDepthVsMapFds verifies that when the deepest map level -// (MapDepth=3) has no index but a shallower level (MapDepth=2) does, -// the generator loop `for i := 0; i < maxDepth-2 && i < len(mapFds)-1; i++` -// does NOT produce extra LevelIndex key structs. +// Test_Fruit5Conf_MapFdsCollection verifies that initLevelMessage only collects +// map keys/fds up to the deepest level that has indexes. This eliminates the +// need for a separate maxDepth variable. // // Fruit5Conf has 3-level map (fruit_map -> country_map -> item_map) but -// indexes only at MapDepth=2 (Country), so maxDepth=2, len(mapFds)=3. -// Without the maxDepth-2 guard, the loop would run 2 iterations instead of 0. -func Test_Fruit5Conf_MaxDepthVsMapFds(t *testing.T) { +// indexes only at MapDepth=2 (Country). The new initLevelMessage logic skips +// item_map (MapDepth=2) because its NextLevel has no indexes, so +// len(mapFds) == 2 (== former maxDepth), and the loop `for i := 0; i < len(mapFds)-2` +// produces 0 iterations — no extra LevelIndex key structs. +func Test_Fruit5Conf_MapFdsCollection(t *testing.T) { descriptor := ParseIndexDescriptor(md[*protoconf.Fruit5Conf]()) - // Simulate initLevelMessage logic to collect maxDepth and mapFds count. - var maxDepth int - var mapFdCount int + // Simulate the new initLevelMessage logic: only collect map keys/fds + // when the next level has any index (NeedGenAnyIndex). + var collectedMapFdCount int + var totalMapFdCount int for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { if fd := levelMessage.FD; fd != nil && fd.IsMap() { - mapFdCount++ - } - if len(levelMessage.Indexes) != 0 || len(levelMessage.OrderedIndexes) != 0 { - maxDepth = levelMessage.MapDepth + totalMapFdCount++ + if levelMessage.NextLevel.NeedGenAnyIndex() { + collectedMapFdCount++ + } } } - // Fruit5Conf: 3 map levels, but deepest index is at MapDepth=2. - assert.Equal(t, 3, mapFdCount, "Fruit5Conf should have 3 map field descriptors") - assert.Equal(t, 2, maxDepth, "Fruit5Conf's deepest index should be at MapDepth=2") - assert.Less(t, maxDepth, mapFdCount, "maxDepth should be less than len(mapFds)") + // Fruit5Conf: 3 total map levels, but only 2 collected (item_map is excluded). + assert.Equal(t, 3, totalMapFdCount, "Fruit5Conf should have 3 total map field descriptors") + assert.Equal(t, 2, collectedMapFdCount, + "Only 2 map fds should be collected (item_map excluded because its NextLevel has no indexes)") - // Verify the LevelIndex key generation loop would produce 0 iterations, - // meaning no extra LevelIndex key structs are generated. + // Verify the LevelIndex key generation loop produces 0 iterations. levelIndexKeyCount := 0 - for i := 0; i < maxDepth-2 && i < mapFdCount-1; i++ { + for i := 0; i < collectedMapFdCount-2; i++ { levelIndexKeyCount++ } assert.Equal(t, 0, levelIndexKeyCount, - "No LevelIndex key structs should be generated: maxDepth-2=%d, len(mapFds)-1=%d", - maxDepth-2, mapFdCount-1) + "No LevelIndex key structs should be generated: len(mapFds)-2=%d", collectedMapFdCount-2) - // Contrast: without the maxDepth-2 guard, using only len(mapFds)-1 - // would incorrectly generate 2 LevelIndex key structs. + // Contrast: if all 3 map fds were collected without filtering, + // the loop would incorrectly generate 1 extra LevelIndex key struct. wrongKeyCount := 0 - for i := 0; i < mapFdCount-1; i++ { + for i := 0; i < totalMapFdCount-2; i++ { wrongKeyCount++ } - assert.Equal(t, 2, wrongKeyCount, - "Without maxDepth guard, %d extra LevelIndex key structs would be generated", wrongKeyCount) + assert.Equal(t, 1, wrongKeyCount, + "Without filtering, %d extra LevelIndex key struct(s) would be generated", wrongKeyCount) } From 5d9aef5ff23d028e5ed1073d684f5e0192ff1ff5 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Wed, 11 Mar 2026 16:53:08 +0800 Subject: [PATCH 06/32] refactor: update MapKey structure and streamline key collection in generator --- .../helper/helper.go | 1 + .../indexes/generator.go | 17 ++++---- .../indexes/index.go | 2 +- .../indexes/ordered_index.go | 2 +- .../helper/helper.go | 3 +- .../indexes/generator.go | 17 ++++---- .../indexes/index.go | 8 ++-- .../indexes/ordered_index.go | 8 ++-- internal/index/descriptor_test.go | 43 +++++++++---------- test/proto/index_conf.proto | 4 +- 10 files changed, 52 insertions(+), 53 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go b/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go index 88d98056..ce3dc6b9 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go +++ b/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go @@ -176,6 +176,7 @@ func ParseLeveledMapPrefix(md protoreflect.MessageDescriptor, mapFd protoreflect type MapKey struct { Type string Name string + Fd protoreflect.FieldDescriptor // the map field descriptor this key belongs to } type MapKeySlice []MapKey diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index c84651a7..c3de573a 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -15,8 +15,7 @@ type Generator struct { message *protogen.Message // level message - keys helper.MapKeySlice - mapFds []protoreflect.FieldDescriptor + keys helper.MapKeySlice } func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { @@ -40,8 +39,8 @@ func (x *Generator) initLevelMessage() { x.keys = x.keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), Name: helper.ParseMapFieldName(fd), + Fd: fd, }) - x.mapFds = append(x.mapFds, fd) } } } @@ -86,17 +85,17 @@ func (x *Generator) GenHppIndexFinders() { return } // The loop generates LevelIndex key types for upper-level map containers. - // initLevelMessage only collects map keys/fds for levels whose deeper - // levels have indexes, so len(x.mapFds) already reflects the effective - // depth. LevelIndex keys start from the 2nd map level (mapFds[1]) onward, - // so the count is len(x.mapFds)-2. - for i := 0; i < len(x.mapFds)-2; i++ { + // initLevelMessage only collects map keys for levels whose deeper + // levels have indexes, so len(x.keys) already reflects the effective + // depth. LevelIndex keys start from the 2nd map level (keys[1]) onward, + // so the count is len(x.keys)-2. + for i := 0; i < len(x.keys)-2; i++ { if i == 0 { x.g.P() x.g.P(helper.Indent(1), "// LevelIndex keys.") x.g.P(" public:") } - fd := x.mapFds[i+1] + fd := x.keys[i+1].Fd keyType := x.levelKeyType(fd) x.g.P(helper.Indent(1), "struct ", keyType, " {") keys := x.keys[:i+2] diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index 262b6791..47085560 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -129,7 +129,7 @@ func (x *Generator) genHppIndexFinders() { if i == 0 { x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.indexContainerName(index, i+1), ";") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.indexContainerName(index, i+1), ";") } } diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index 878d7ecc..53b7c47a 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -119,7 +119,7 @@ func (x *Generator) genHppOrderedIndexFinders() { if i == 0 { x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.orderedIndexContainerName(index, i+1), ";") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.orderedIndexContainerName(index, i+1), ";") } } diff --git a/cmd/protoc-gen-go-tableau-loader/helper/helper.go b/cmd/protoc-gen-go-tableau-loader/helper/helper.go index 79fc79ad..99ebcd69 100644 --- a/cmd/protoc-gen-go-tableau-loader/helper/helper.go +++ b/cmd/protoc-gen-go-tableau-loader/helper/helper.go @@ -261,7 +261,8 @@ func ParseLeveledMapPrefix(md protoreflect.MessageDescriptor, mapFd protoreflect type MapKey struct { Type string Name string - FieldName string // multi-colunm index only + FieldName string // multi-colunm index only + Fd protoreflect.FieldDescriptor // the map field descriptor this key belongs to } type MapKeySlice []MapKey diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index d12178ac..a6317b9c 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -16,8 +16,7 @@ type Generator struct { message *protogen.Message // level message - keys helper.MapKeySlice - mapFds []protoreflect.FieldDescriptor + keys helper.MapKeySlice } func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { @@ -43,8 +42,8 @@ func (x *Generator) initLevelMessage() { Type: helper.ParseMapKeyType(fd.MapKey()), Name: helper.ParseMapFieldNameAsFuncParam(fd), FieldName: helper.ParseMapFieldNameAsKeyStructFieldName(fd), + Fd: fd, }) - x.mapFds = append(x.mapFds, fd) } } } @@ -89,16 +88,16 @@ func (x *Generator) GenIndexTypeDef() { return } // The loop generates LevelIndex key types for upper-level map containers. - // initLevelMessage only collects map keys/fds for levels whose deeper - // levels have indexes, so len(x.mapFds) already reflects the effective - // depth. LevelIndex keys start from the 2nd map level (mapFds[1]) onward, - // so the count is len(x.mapFds)-2. - for i := 0; i < len(x.mapFds)-2; i++ { + // initLevelMessage only collects map keys for levels whose deeper + // levels have indexes, so len(x.keys) already reflects the effective + // depth. LevelIndex keys start from the 2nd map level (keys[1]) onward, + // so the count is len(x.keys)-2. + for i := 0; i < len(x.keys)-2; i++ { if i == 0 { x.g.P() x.g.P("// LevelIndex keys.") } - fd := x.mapFds[i+1] + fd := x.keys[i+1].Fd keyType := x.levelKeyType(fd) keys := x.keys[:i+2] x.g.P("type ", keyType, " struct {") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index 7c395fd9..d05bd9f7 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -90,7 +90,7 @@ func (x *Generator) genIndexField() { if i == 0 { x.g.P(x.indexContainerName(index, i+1), " map[", x.keys[0].Type, "]", x.indexMapType(index)) } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) x.g.P(x.indexContainerName(index, i+1), " map[", levelIndexKeyType, "]", x.indexMapType(index)) } } @@ -114,7 +114,7 @@ func (x *Generator) genIndexLoader() { if i == 0 { x.g.P("x.", x.indexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]", x.indexMapType(index), ")") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) x.g.P("x.", x.indexContainerName(index, i+1), " = make(map[", levelIndexKeyType, "]", x.indexMapType(index), ")") } } @@ -212,7 +212,7 @@ func (x *Generator) genIndexLoaderCommon(levelMessage *index.LevelMessage, index for j := 1; j <= i+1; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) keyName := indexContainerName + "Keys" x.g.P(keyName, " := ", levelIndexKeyType, "{", strings.Join(fields, ", "), "}") x.g.P("if x.", indexContainerName, "[", keyName, "] == nil {") @@ -320,7 +320,7 @@ func (x *Generator) genIndexFinders() { if len(partKeys) == 1 { x.g.P("return x.", indexContainerName, "[", partArgs, "]") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) x.g.P("return x.", indexContainerName, "[", levelIndexKeyType, "{", partArgs, "}]") } x.g.P("}") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go index 0c111ff9..84ecbfb8 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go @@ -114,7 +114,7 @@ func (x *Generator) genOrderedIndexField() { if i == 0 { x.g.P(x.orderedIndexContainerName(index, i+1), " map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index)) } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) x.g.P(x.orderedIndexContainerName(index, i+1), " map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index)) } } @@ -138,7 +138,7 @@ func (x *Generator) genOrderedIndexLoader() { if i == 0 { x.g.P("x.", x.orderedIndexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index), ")") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) x.g.P("x.", x.orderedIndexContainerName(index, i+1), " = make(map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index), ")") } } @@ -240,7 +240,7 @@ func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage for j := 1; j <= i+1; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) keyName := orderedIndexContainerName + "Keys" x.g.P(keyName, " := ", levelIndexKeyType, "{", strings.Join(fields, ", "), "}") x.g.P("if x.", orderedIndexContainerName, "[", keyName, "] == nil {") @@ -352,7 +352,7 @@ func (x *Generator) genOrderedIndexFinders() { if len(partKeys) == 1 { x.g.P("return x.", orderedIndexContainerName, "[", partArgs, "]") } else { - levelIndexKeyType := x.levelKeyType(x.mapFds[i]) + levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) x.g.P("return x.", orderedIndexContainerName, "[", levelIndexKeyType, "{", partArgs, "}]") } x.g.P("}") diff --git a/internal/index/descriptor_test.go b/internal/index/descriptor_test.go index 974a6c7a..56d5a88c 100644 --- a/internal/index/descriptor_test.go +++ b/internal/index/descriptor_test.go @@ -593,8 +593,8 @@ func Test_ParseIndexDescriptor(t *testing.T) { // Fruit5Conf: 3-level map (fruit_map -> country_map -> item_map), // but indexes only at MapDepth=2 (Country level). // The 3rd level map (MapDepth=3, Item) has no index. - // This validates that initLevelMessage only collects map keys/fds - // up to the deepest level that has indexes, so len(mapFds) == 2 + // This validates that initLevelMessage only collects map keys + // for levels whose deeper levels have indexes, so len(keys) == 2 // (not 3), preventing generation of extra LevelIndex key structs. name: "Fruit5Conf", args: args{ @@ -648,48 +648,47 @@ func Test_ParseIndexDescriptor(t *testing.T) { } } -// Test_Fruit5Conf_MapFdsCollection verifies that initLevelMessage only collects -// map keys/fds up to the deepest level that has indexes. This eliminates the -// need for a separate maxDepth variable. +// Test_Fruit5Conf_MapKeysCollection verifies that initLevelMessage only collects +// map keys for levels whose deeper levels have indexes. // -// Fruit5Conf has 3-level map (fruit_map -> country_map -> item_map) but -// indexes only at MapDepth=2 (Country). The new initLevelMessage logic skips -// item_map (MapDepth=2) because its NextLevel has no indexes, so -// len(mapFds) == 2 (== former maxDepth), and the loop `for i := 0; i < len(mapFds)-2` -// produces 0 iterations — no extra LevelIndex key structs. -func Test_Fruit5Conf_MapFdsCollection(t *testing.T) { +// Fruit5Conf has a 3-level map (fruit_map -> country_map -> item_map) but +// indexes only at MapDepth=2 (Country). initLevelMessage skips item_map +// because its NextLevel has no indexes, so len(keys) == 2, and the loop +// `for i := 0; i < len(keys)-2` produces 0 iterations — no extra +// LevelIndex key structs are generated. +func Test_Fruit5Conf_MapKeysCollection(t *testing.T) { descriptor := ParseIndexDescriptor(md[*protoconf.Fruit5Conf]()) - // Simulate the new initLevelMessage logic: only collect map keys/fds + // Simulate the initLevelMessage logic: only collect map keys // when the next level has any index (NeedGenAnyIndex). - var collectedMapFdCount int - var totalMapFdCount int + var collectedMapKeyCount int + var totalMapKeyCount int for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { if fd := levelMessage.FD; fd != nil && fd.IsMap() { - totalMapFdCount++ + totalMapKeyCount++ if levelMessage.NextLevel.NeedGenAnyIndex() { - collectedMapFdCount++ + collectedMapKeyCount++ } } } // Fruit5Conf: 3 total map levels, but only 2 collected (item_map is excluded). - assert.Equal(t, 3, totalMapFdCount, "Fruit5Conf should have 3 total map field descriptors") - assert.Equal(t, 2, collectedMapFdCount, - "Only 2 map fds should be collected (item_map excluded because its NextLevel has no indexes)") + assert.Equal(t, 3, totalMapKeyCount, "Fruit5Conf should have 3 total map levels") + assert.Equal(t, 2, collectedMapKeyCount, + "Only 2 map keys should be collected (item_map excluded because its NextLevel has no indexes)") // Verify the LevelIndex key generation loop produces 0 iterations. levelIndexKeyCount := 0 - for i := 0; i < collectedMapFdCount-2; i++ { + for i := 0; i < collectedMapKeyCount-2; i++ { levelIndexKeyCount++ } assert.Equal(t, 0, levelIndexKeyCount, - "No LevelIndex key structs should be generated: len(mapFds)-2=%d", collectedMapFdCount-2) + "No LevelIndex key structs should be generated: len(keys)-2=%d", collectedMapKeyCount-2) // Contrast: if all 3 map fds were collected without filtering, // the loop would incorrectly generate 1 extra LevelIndex key struct. wrongKeyCount := 0 - for i := 0; i < totalMapFdCount-2; i++ { + for i := 0; i < totalMapKeyCount-2; i++ { wrongKeyCount++ } assert.Equal(t, 1, wrongKeyCount, diff --git a/test/proto/index_conf.proto b/test/proto/index_conf.proto index b6f45c16..802364f8 100644 --- a/test/proto/index_conf.proto +++ b/test/proto/index_conf.proto @@ -121,8 +121,8 @@ message Fruit4Conf { // Nesting: map -> map -> map // 3-level map but indexes only at the 2nd level (MapDepth=2). // The 3rd level map (MapDepth=3) has no index. -// This tests that maxDepth (=2) < len(mapFds) (=3) does not cause -// generation of extra LevelIndex key structs. +// This tests that when indexes exist only at the 2nd level (not the 3rd), +// no extra LevelIndex key structs are generated. message Fruit5Conf { option (tableau.worksheet) = { name: "Fruit5Conf" From 3eb699f3289f1f2e0de533b31f774aea38bbfb5d Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Wed, 11 Mar 2026 17:28:01 +0800 Subject: [PATCH 07/32] refactor: remove unused map key checks and streamline index loading logic --- .../indexes/generator.go | 22 ---------------- .../indexes/index.go | 2 +- .../indexes/ordered_index.go | 2 +- .../indexes/generator.go | 22 ---------------- .../indexes/index.go | 2 +- .../indexes/ordered_index.go | 2 +- internal/index/descriptor.go | 25 +++++++++++++++++++ 7 files changed, 29 insertions(+), 48 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index c3de573a..5a37fc50 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -129,25 +129,3 @@ func (x *Generator) GenCppIndexFinders() { x.genCppIndexFinders() x.genCppOrderedIndexFinders() } - -// needMapKeyForIndex checks if the map key variable at the given mapDepth -// is needed by any subsequent regular index's leveled containers. -func (x *Generator) needMapKeyForIndex(mapDepth int) bool { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.Indexes) > 0 && levelMessage.NumLeveledContainers() > mapDepth { - return true - } - } - return false -} - -// needMapKeyForOrderedIndex checks if the map key variable at the given mapDepth -// is needed by any subsequent ordered index's leveled containers. -func (x *Generator) needMapKeyForOrderedIndex(mapDepth int) bool { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.OrderedIndexes) > 0 && levelMessage.NumLeveledContainers() > mapDepth { - return true - } - } - return false -} diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index 47085560..69f67269 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -169,7 +169,7 @@ func (x *Generator) genIndexLoader() { x.g.P(helper.Indent(levelMessage.Depth+1), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") parentDataName = itemName if levelMessage.FD.IsMap() { - if x.needMapKeyForIndex(levelMessage.MapDepth) { + if levelMessage.NeedMapKeyForIndex() { x.g.P(helper.Indent(levelMessage.Depth+2), "auto k", levelMessage.MapDepth+1, " = ", itemName, ".first;") } parentDataName = itemName + ".second" diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index 53b7c47a..bf22a327 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -159,7 +159,7 @@ func (x *Generator) genOrderedIndexLoader() { x.g.P(helper.Indent(levelMessage.Depth+1), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") parentDataName = itemName if levelMessage.FD.IsMap() { - if x.needMapKeyForOrderedIndex(levelMessage.MapDepth) { + if levelMessage.NeedMapKeyForOrderedIndex() { x.g.P(helper.Indent(levelMessage.Depth+2), "auto k", levelMessage.MapDepth+1, " = ", itemName, ".first;") } parentDataName = itemName + ".second" diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index a6317b9c..a34ce2d9 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -124,25 +124,3 @@ func (x *Generator) GenIndexFinders() { x.genIndexFinders() x.genOrderedIndexFinders() } - -// needMapKeyForIndex checks if the map key variable at the given mapDepth -// is needed by any subsequent regular index's leveled containers. -func (x *Generator) needMapKeyForIndex(mapDepth int) bool { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.Indexes) > 0 && levelMessage.NumLeveledContainers() > mapDepth { - return true - } - } - return false -} - -// needMapKeyForOrderedIndex checks if the map key variable at the given mapDepth -// is needed by any subsequent ordered index's leveled containers. -func (x *Generator) needMapKeyForOrderedIndex(mapDepth int) bool { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if len(levelMessage.OrderedIndexes) > 0 && levelMessage.NumLeveledContainers() > mapDepth { - return true - } - } - return false -} diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index d05bd9f7..f4dff7d8 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -134,7 +134,7 @@ func (x *Generator) genIndexLoader() { break } if levelMessage.FD.IsMap() { - if x.needMapKeyForIndex(levelMessage.MapDepth) { + if levelMessage.NeedMapKeyForIndex() { x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") } else { x.g.P("for _, ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go index 84ecbfb8..9078a7ed 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go @@ -158,7 +158,7 @@ func (x *Generator) genOrderedIndexLoader() { break } if levelMessage.FD.IsMap() { - if x.needMapKeyForOrderedIndex(levelMessage.MapDepth) { + if levelMessage.NeedMapKeyForOrderedIndex() { x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") } else { x.g.P("for _, ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index af55ace7..58dc192b 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -93,6 +93,31 @@ func (l *LevelMessage) NeedGenAnyIndex() bool { return l.NeedGenIndex() || l.NeedGenOrderedIndex() } +// NeedMapKeyForIndex checks if the map key variable at this level is needed +// by any deeper level's regular index's leveled containers. +// It finds the first level whose MapDepth > l.MapDepth+1 (i.e., at least 2 map +// levels deeper), then delegates to NeedGenIndex which recursively checks that +// level and all deeper levels for indexes. +func (l *LevelMessage) NeedMapKeyForIndex() bool { + for lm := l.NextLevel; lm != nil; lm = lm.NextLevel { + if lm.MapDepth > l.MapDepth+1 { + return lm.NeedGenIndex() + } + } + return false +} + +// NeedMapKeyForOrderedIndex checks if the map key variable at this level is +// needed by any deeper level's ordered index's leveled containers. +func (l *LevelMessage) NeedMapKeyForOrderedIndex() bool { + for lm := l.NextLevel; lm != nil; lm = lm.NextLevel { + if lm.MapDepth > l.MapDepth+1 { + return lm.NeedGenOrderedIndex() + } + } + return false +} + type LevelIndex struct { *Index MD protoreflect.MessageDescriptor From 387e4b58fbddebd2076fea6fe147408eee3f3028 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Wed, 11 Mar 2026 17:34:23 +0800 Subject: [PATCH 08/32] refactor: remove redundant key checks in index and ordered index generation --- .../indexes/index.go | 18 ------------------ .../indexes/ordered_index.go | 18 ------------------ .../indexes/index.go | 15 --------------- .../indexes/ordered_index.go | 15 --------------- 4 files changed, 66 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index 69f67269..a8f3a046 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -105,9 +105,6 @@ func (x *Generator) genHppIndexFinders() { x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } partKeys := x.keys[:i+1] x.g.P(helper.Indent(1), "// Finds the index: key(", index.Index, ") to value(", vectorType, "),") x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i+1), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") @@ -123,9 +120,6 @@ func (x *Generator) genHppIndexFinders() { x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index, 0), ";") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } if i == 0 { x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.indexContainerName(index, i+1), ";") } else { @@ -147,9 +141,6 @@ func (x *Generator) genIndexLoader() { for _, index := range levelMessage.Indexes { x.g.P(helper.Indent(1), x.indexContainerName(index, 0), ".clear();") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } x.g.P(helper.Indent(1), x.indexContainerName(index, i+1), ".clear();") } } @@ -236,9 +227,6 @@ func (x *Generator) generateOneCppMulticolumnIndex(levelMessage *index.LevelMess func (x *Generator) genLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.indexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } if i == 0 { x.g.P(helper.Indent(ident), x.indexContainerName(index, i+1), "[k1][", key, "].push_back(&", parentDataName, ");") } else { @@ -278,9 +266,6 @@ func (x *Generator) genIndexSorter() { x.g.P(helper.Indent(1), "}") // Iterate all leveled containers. for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index, i+1), ") {") x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") @@ -333,9 +318,6 @@ func (x *Generator) genCppIndexFinders() { x.g.P() for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } indexContainerName := x.indexContainerName(index, i+1) partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index bf22a327..8cf66d4e 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -95,9 +95,6 @@ func (x *Generator) genHppOrderedIndexFinders() { x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } partKeys := x.keys[:i+1] x.g.P(helper.Indent(1), "// Finds the ordered index: key(", index.Index, ") to value(", vectorType, "),") x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partKeys.GenGetArguments(), ").") @@ -113,9 +110,6 @@ func (x *Generator) genHppOrderedIndexFinders() { x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.orderedIndexContainerName(index, 0), ";") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } if i == 0 { x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.orderedIndexContainerName(index, i+1), ";") } else { @@ -137,9 +131,6 @@ func (x *Generator) genOrderedIndexLoader() { for _, index := range levelMessage.OrderedIndexes { x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, 0), ".clear();") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, i+1), ".clear();") } } @@ -226,9 +217,6 @@ func (x *Generator) generateOneCppMulticolumnOrderedIndex(levelMessage *index.Le func (x *Generator) genOrderedLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } if i == 0 { x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i+1), "[k1][", key, "].push_back(&", parentDataName, ");") } else { @@ -268,9 +256,6 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P(helper.Indent(1), "}") // Iterate all leveled containers. for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } x.g.P(helper.Indent(1), "for (auto&& item : ", x.orderedIndexContainerName(index, i+1), ") {") x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") @@ -323,9 +308,6 @@ func (x *Generator) genCppOrderedIndexFinders() { x.g.P() for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } indexContainerNameI := x.orderedIndexContainerName(index, i+1) partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index f4dff7d8..932f9432 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -84,9 +84,6 @@ func (x *Generator) genIndexField() { for _, index := range levelMessage.Indexes { x.g.P(x.indexContainerName(index, 0), " ", x.indexMapType(index)) for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } if i == 0 { x.g.P(x.indexContainerName(index, i+1), " map[", x.keys[0].Type, "]", x.indexMapType(index)) } else { @@ -108,9 +105,6 @@ func (x *Generator) genIndexLoader() { for _, index := range levelMessage.Indexes { x.g.P("x.", x.indexContainerName(index, 0), " = make(", x.indexMapType(index), ")") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } if i == 0 { x.g.P("x.", x.indexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]", x.indexMapType(index), ")") } else { @@ -198,9 +192,6 @@ func (x *Generator) genIndexLoaderCommon(levelMessage *index.LevelMessage, index indexContainerName := x.indexContainerName(index, 0) x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } indexContainerName := x.indexContainerName(index, i+1) if i == 0 { x.g.P("if x.", indexContainerName, "[k1] == nil {") @@ -248,9 +239,6 @@ func (x *Generator) genIndexSorter() { x.g.P("}") // Iterate all leveled containers. for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } x.g.P("for _, itemMap := range x.", x.indexContainerName(index, i+1), " {") x.g.P("for _, itemList := range itemMap {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") @@ -305,9 +293,6 @@ func (x *Generator) genIndexFinders() { x.g.P() for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } indexContainerName := x.indexContainerName(index, i+1) partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go index 9078a7ed..b5a3ba7d 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go @@ -108,9 +108,6 @@ func (x *Generator) genOrderedIndexField() { for _, index := range levelMessage.OrderedIndexes { x.g.P(x.orderedIndexContainerName(index, 0), " *", x.orderedIndexMapType(index)) for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } if i == 0 { x.g.P(x.orderedIndexContainerName(index, i+1), " map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index)) } else { @@ -132,9 +129,6 @@ func (x *Generator) genOrderedIndexLoader() { for _, index := range levelMessage.OrderedIndexes { x.g.P("x.", x.orderedIndexContainerName(index, 0), " = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } if i == 0 { x.g.P("x.", x.orderedIndexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index), ")") } else { @@ -224,9 +218,6 @@ func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage x.g.P("value, _ := x.", indexContainerName, ".Get(key)") x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } orderedIndexContainerName := x.orderedIndexContainerName(index, i+1) valueName := orderedIndexContainerName + "Value" if i == 0 { @@ -278,9 +269,6 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P("})") // Iterate all leveled containers. for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } x.g.P("for _, itemMap := range x.", x.orderedIndexContainerName(index, i+1), " {") x.g.P("itemMap.Range(func(key ", x.orderedIndexMapKeyType(index), ", itemList []*", x.mapValueType(index), ") bool {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") @@ -337,9 +325,6 @@ func (x *Generator) genOrderedIndexFinders() { x.g.P() for i := 0; i < levelMessage.NumLeveledContainers(); i++ { - if i >= len(x.keys) { - break - } orderedIndexContainerName := x.orderedIndexContainerName(index, i+1) partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() From 78affcf9c4ec66595f832dc10e4bd7a419b503ff Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 15:40:39 +0800 Subject: [PATCH 09/32] improve: enhance comments with examples for LevelIndex key struct generation logic --- .../indexes/generator.go | 21 ++++++++++++++----- .../indexes/index.go | 12 +++++------ .../indexes/ordered_index.go | 12 +++++------ .../indexes/generator.go | 21 ++++++++++++++----- .../indexes/index.go | 10 ++++----- .../indexes/ordered_index.go | 10 ++++----- internal/index/descriptor.go | 12 ++++------- third_party/_submodules/tableau | 2 +- 8 files changed, 59 insertions(+), 41 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index 5a37fc50..84e9e65b 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -84,11 +84,22 @@ func (x *Generator) GenHppIndexFinders() { if !x.NeedGenerate() { return } - // The loop generates LevelIndex key types for upper-level map containers. - // initLevelMessage only collects map keys for levels whose deeper - // levels have indexes, so len(x.keys) already reflects the effective - // depth. LevelIndex keys start from the 2nd map level (keys[1]) onward, - // so the count is len(x.keys)-2. + // Generate LevelIndex key structs for intermediate map levels. + // + // x.keys holds one entry per effective map level (only levels that lead to + // an index are included by initLevelMessage). For a 3-level map keyed by + // (k1, k2, k3) with an index at the deepest level, x.keys = [k1, k2, k3]. + // + // The deepest level's key (k3) is the direct lookup key and does not need + // a LevelIndex struct. The top-level key (k1, i.e. keys[0]) is a plain map + // key and also does not need one. Only the intermediate levels (keys[1] … + // keys[len-2]) require a LevelIndex struct that bundles all ancestor keys: + // + // keys = [k1, k2, k3] → one struct for keys[1]=k2: { k1, k2 } + // keys = [k1,k2,k3,k4] → structs for keys[1]=k2: {k1,k2} + // and keys[2]=k3: {k1,k2,k3} + // + // Hence the loop runs len(x.keys)-2 times (0 times when len ≤ 2). for i := 0; i < len(x.keys)-2; i++ { if i == 0 { x.g.P() diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index a8f3a046..6eb25051 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -104,7 +104,7 @@ func (x *Generator) genHppIndexFinders() { x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { partKeys := x.keys[:i+1] x.g.P(helper.Indent(1), "// Finds the index: key(", index.Index, ") to value(", vectorType, "),") x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i+1), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") @@ -119,7 +119,7 @@ func (x *Generator) genHppIndexFinders() { x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index, 0), ";") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { if i == 0 { x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.indexContainerName(index, i+1), ";") } else { @@ -140,7 +140,7 @@ func (x *Generator) genIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P(helper.Indent(1), x.indexContainerName(index, 0), ".clear();") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { x.g.P(helper.Indent(1), x.indexContainerName(index, i+1), ".clear();") } } @@ -226,7 +226,7 @@ func (x *Generator) generateOneCppMulticolumnIndex(levelMessage *index.LevelMess func (x *Generator) genLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.indexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { if i == 0 { x.g.P(helper.Indent(ident), x.indexContainerName(index, i+1), "[k1][", key, "].push_back(&", parentDataName, ");") } else { @@ -265,7 +265,7 @@ func (x *Generator) genIndexSorter() { x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(1), "}") // Iterate all leveled containers. - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index, i+1), ") {") x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") @@ -317,7 +317,7 @@ func (x *Generator) genCppIndexFinders() { x.g.P("}") x.g.P() - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { indexContainerName := x.indexContainerName(index, i+1) partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index 8cf66d4e..d13613c8 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -94,7 +94,7 @@ func (x *Generator) genHppOrderedIndexFinders() { x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { partKeys := x.keys[:i+1] x.g.P(helper.Indent(1), "// Finds the ordered index: key(", index.Index, ") to value(", vectorType, "),") x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partKeys.GenGetArguments(), ").") @@ -109,7 +109,7 @@ func (x *Generator) genHppOrderedIndexFinders() { x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.orderedIndexContainerName(index, 0), ";") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { if i == 0 { x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.orderedIndexContainerName(index, i+1), ";") } else { @@ -130,7 +130,7 @@ func (x *Generator) genOrderedIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, 0), ".clear();") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, i+1), ".clear();") } } @@ -216,7 +216,7 @@ func (x *Generator) generateOneCppMulticolumnOrderedIndex(levelMessage *index.Le func (x *Generator) genOrderedLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { if i == 0 { x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i+1), "[k1][", key, "].push_back(&", parentDataName, ");") } else { @@ -255,7 +255,7 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(1), "}") // Iterate all leveled containers. - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { x.g.P(helper.Indent(1), "for (auto&& item : ", x.orderedIndexContainerName(index, i+1), ") {") x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") @@ -307,7 +307,7 @@ func (x *Generator) genCppOrderedIndexFinders() { x.g.P("}") x.g.P() - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { indexContainerNameI := x.orderedIndexContainerName(index, i+1) partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index a34ce2d9..9fc0d738 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -87,11 +87,22 @@ func (x *Generator) GenIndexTypeDef() { if !x.NeedGenerate() { return } - // The loop generates LevelIndex key types for upper-level map containers. - // initLevelMessage only collects map keys for levels whose deeper - // levels have indexes, so len(x.keys) already reflects the effective - // depth. LevelIndex keys start from the 2nd map level (keys[1]) onward, - // so the count is len(x.keys)-2. + // Generate LevelIndex key structs for intermediate map levels. + // + // x.keys holds one entry per effective map level (only levels that lead to + // an index are included by initLevelMessage). For a 3-level map keyed by + // (k1, k2, k3) with an index at the deepest level, x.keys = [k1, k2, k3]. + // + // The deepest level's key (k3) is the direct lookup key and does not need + // a LevelIndex struct. The top-level key (k1, i.e. keys[0]) is a plain map + // key and also does not need one. Only the intermediate levels (keys[1] … + // keys[len-2]) require a LevelIndex struct that bundles all ancestor keys: + // + // keys = [k1, k2, k3] → one struct for keys[1]=k2: { k1, k2 } + // keys = [k1,k2,k3,k4] → structs for keys[1]=k2: {k1,k2} + // and keys[2]=k3: {k1,k2,k3} + // + // Hence the loop runs len(x.keys)-2 times (0 times when len ≤ 2). for i := 0; i < len(x.keys)-2; i++ { if i == 0 { x.g.P() diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index 932f9432..45e9652a 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -83,7 +83,7 @@ func (x *Generator) genIndexField() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P(x.indexContainerName(index, 0), " ", x.indexMapType(index)) - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { if i == 0 { x.g.P(x.indexContainerName(index, i+1), " map[", x.keys[0].Type, "]", x.indexMapType(index)) } else { @@ -104,7 +104,7 @@ func (x *Generator) genIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.Indexes { x.g.P("x.", x.indexContainerName(index, 0), " = make(", x.indexMapType(index), ")") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { if i == 0 { x.g.P("x.", x.indexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]", x.indexMapType(index), ")") } else { @@ -191,7 +191,7 @@ func (x *Generator) generateOneMulticolumnIndex(levelMessage *index.LevelMessage func (x *Generator) genIndexLoaderCommon(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { indexContainerName := x.indexContainerName(index, 0) x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { indexContainerName := x.indexContainerName(index, i+1) if i == 0 { x.g.P("if x.", indexContainerName, "[k1] == nil {") @@ -238,7 +238,7 @@ func (x *Generator) genIndexSorter() { x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("}") // Iterate all leveled containers. - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { x.g.P("for _, itemMap := range x.", x.indexContainerName(index, i+1), " {") x.g.P("for _, itemList := range itemMap {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") @@ -292,7 +292,7 @@ func (x *Generator) genIndexFinders() { x.g.P("}") x.g.P() - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { indexContainerName := x.indexContainerName(index, i+1) partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go index b5a3ba7d..aaeff57b 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go @@ -107,7 +107,7 @@ func (x *Generator) genOrderedIndexField() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P(x.orderedIndexContainerName(index, 0), " *", x.orderedIndexMapType(index)) - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { if i == 0 { x.g.P(x.orderedIndexContainerName(index, i+1), " map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index)) } else { @@ -128,7 +128,7 @@ func (x *Generator) genOrderedIndexLoader() { for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { for _, index := range levelMessage.OrderedIndexes { x.g.P("x.", x.orderedIndexContainerName(index, 0), " = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { if i == 0 { x.g.P("x.", x.orderedIndexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index), ")") } else { @@ -217,7 +217,7 @@ func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage indexContainerName := x.orderedIndexContainerName(index, 0) x.g.P("value, _ := x.", indexContainerName, ".Get(key)") x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { orderedIndexContainerName := x.orderedIndexContainerName(index, i+1) valueName := orderedIndexContainerName + "Value" if i == 0 { @@ -268,7 +268,7 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P("return true") x.g.P("})") // Iterate all leveled containers. - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { x.g.P("for _, itemMap := range x.", x.orderedIndexContainerName(index, i+1), " {") x.g.P("itemMap.Range(func(key ", x.orderedIndexMapKeyType(index), ", itemList []*", x.mapValueType(index), ") bool {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") @@ -324,7 +324,7 @@ func (x *Generator) genOrderedIndexFinders() { x.g.P("}") x.g.P() - for i := 0; i < levelMessage.NumLeveledContainers(); i++ { + for i := 0; i < levelMessage.UpperMapLevels(); i++ { orderedIndexContainerName := x.orderedIndexContainerName(index, i+1) partKeys := x.keys[:i+1] partParams := partKeys.GenGetParams() diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index 58dc192b..82f382cd 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -62,14 +62,10 @@ type LevelMessage struct { MapDepth int } -// NumLeveledContainers returns the number of upper-level index containers -// for this level message. It equals MapDepth - 1 because: -// - MapDepth is 0-based (starts from 0 for the top-level message) -// - The current level itself is excluded (only parent levels count) -// -// For example, a 3-level nested map (MapDepth=2) has 1 upper-level container (keyed by k1), -// and a 4-level nested map (MapDepth=3) has 2 upper-level containers (keyed by k1 and {k1, k2}). -func (l *LevelMessage) NumLeveledContainers() int { +// UpperMapLevels returns the number of upper map levels above this level. +// For example, an index at map depth 2 has 1 upper map layer (keyed by k1), +// and an index at map depth 3 has 2 upper map levels (keyed by k1 and {k1, k2}). +func (l *LevelMessage) UpperMapLevels() int { return l.MapDepth - 1 } diff --git a/third_party/_submodules/tableau b/third_party/_submodules/tableau index fe374083..0d308e52 160000 --- a/third_party/_submodules/tableau +++ b/third_party/_submodules/tableau @@ -1 +1 @@ -Subproject commit fe374083bb58a917c1937f3d602585ad3787da4d +Subproject commit 0d308e52f7ca68ae770d7da40598ef84c7136fb3 From 15fa91c4afbaa5990fe0f08bd95193818c4fd1a5 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 16:51:26 +0800 Subject: [PATCH 10/32] ci: add testing-go workflow and update init scripts --- .github/workflows/release-cpp.yml | 4 +- .github/workflows/release-go.yml | 4 +- .github/workflows/testing-go.yml | 64 ++++++++++++++ .gitignore | 1 + README.md | 2 +- test/go-tableau-loader/gen.sh | 18 ++-- .../protoconf/loader/hub.pc.go | 56 ++++++------- .../protoconf/loader/messager_container.pc.go | 84 +++++++++---------- 8 files changed, 151 insertions(+), 82 deletions(-) create mode 100644 .github/workflows/testing-go.yml diff --git a/.github/workflows/release-cpp.yml b/.github/workflows/release-cpp.yml index 08cd71ed..34792cca 100644 --- a/.github/workflows/release-cpp.yml +++ b/.github/workflows/release-cpp.yml @@ -27,7 +27,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: "1.20.x" + go-version: "1.21.x" - name: Download dependencies run: | @@ -59,4 +59,4 @@ jobs: upload_url: ${{ github.event.release.upload_url }} asset_path: ./${{ steps.package.outputs.name }} asset_name: ${{ steps.package.outputs.name }} - asset_content_type: application/gzip \ No newline at end of file + asset_content_type: application/gzip diff --git a/.github/workflows/release-go.yml b/.github/workflows/release-go.yml index 137b55a2..68da1559 100644 --- a/.github/workflows/release-go.yml +++ b/.github/workflows/release-go.yml @@ -26,7 +26,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: "1.20.x" + go-version: "1.21.x" - name: Download dependencies run: | @@ -58,4 +58,4 @@ jobs: upload_url: ${{ github.event.release.upload_url }} asset_path: ./${{ steps.package.outputs.name }} asset_name: ${{ steps.package.outputs.name }} - asset_content_type: application/gzip \ No newline at end of file + asset_content_type: application/gzip diff --git a/.github/workflows/testing-go.yml b/.github/workflows/testing-go.yml new file mode 100644 index 00000000..25de3304 --- /dev/null +++ b/.github/workflows/testing-go.yml @@ -0,0 +1,64 @@ +name: Testing Go + +# Trigger on pushes, PRs (excluding documentation changes), and nightly. +on: + push: + branches: [master, main] + pull_request: + schedule: + - cron: 0 0 * * * # daily at 00:00 + workflow_dispatch: + +permissions: + contents: read + +jobs: + test: + strategy: + matrix: + go-version: [1.21.x, 1.23.x] + os: [ubuntu-latest, windows-latest] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout Code + uses: actions/checkout@v6 + + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: ${{ matrix.go-version }} + cache: true + + - name: Install Protoc + uses: arduino/setup-protoc@v3 + with: + version: "23.x" + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install protoc-gen-go + run: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.2 + + - name: Init Git submodules + run: git submodule update --init --recursive + + - name: Generate protoconf + shell: bash + run: PROTOC=protoc bash test/go-tableau-loader/gen.sh + + - name: Vet + run: go vet ./... + + - name: Unittest + run: go test -v -timeout 30m -race ./... -coverprofile=coverage.txt -covermode=atomic + + - name: Run loader + working-directory: test/go-tableau-loader + run: go run . + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + slug: tableauio/loader diff --git a/.gitignore b/.gitignore index f686f554..9c0a1db2 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ cmd/protoc-gen-go-tableau-loader/protoc-gen-go-tableau-loader test/go-tableau-loader/go-tableau-loader _lab/ts/src/protoconf +coverage.txt diff --git a/README.md b/README.md index d64ca672..45baa4a3 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ The official config loader for [Tableau](https://github.com/tableauio/tableau). - Install protoc-gen-go: `go install google.golang.org/protobuf/cmd/protoc-gen-go@latest` - Change dir: `cd test/go-tableau-loader` - Generate protoconf: `bash ./gen.sh` -- Build: `go build` +- Run: `go run .` ### References diff --git a/test/go-tableau-loader/gen.sh b/test/go-tableau-loader/gen.sh index e547ff5e..bca213dd 100755 --- a/test/go-tableau-loader/gen.sh +++ b/test/go-tableau-loader/gen.sh @@ -4,13 +4,14 @@ set -e set -o pipefail -shopt -s globstar - cd "$(git rev-parse --show-toplevel)" -PROTOC="./third_party/_submodules/protobuf/cmake/build/protoc" + +# Allow overriding protoc via environment variable. +# Default to the locally compiled protoc for local development. +PROTOC="${PROTOC:-./third_party/_submodules/protobuf/cmake/build/protoc}" PROTOBUF_PROTO="./third_party/_submodules/protobuf/src" TABLEAU_PROTO="./third_party/_submodules/tableau/proto" -PLGUIN_DIR="./cmd/protoc-gen-go-tableau-loader" +PLUGIN_DIR="./cmd/protoc-gen-go-tableau-loader" PROTOCONF_IN="./test/proto" PROTOCONF_OUT="./test/go-tableau-loader/protoconf" LOADER_OUT="$PROTOCONF_OUT/loader" @@ -20,9 +21,12 @@ rm -rfv "$PROTOCONF_OUT" "$LOADER_OUT" mkdir -p "$PROTOCONF_OUT" "$LOADER_OUT" # build -cd "${PLGUIN_DIR}" && go build && cd - +cd "${PLUGIN_DIR}" && go build && cd - + +export PATH="$(pwd)/${PLUGIN_DIR}:${PATH}" -export PATH="${PLGUIN_DIR}:${PATH}" +# Collect all .proto files (use `find` for cross-platform compatibility). +PROTO_FILES=$(find "$PROTOCONF_IN" -name "*.proto") ${PROTOC} \ --go-tableau-loader_out="$LOADER_OUT" \ @@ -32,4 +36,4 @@ ${PROTOC} \ --proto_path="$PROTOBUF_PROTO" \ --proto_path="$TABLEAU_PROTO" \ --proto_path="$PROTOCONF_IN" \ - "$PROTOCONF_IN"/**/*.proto + $PROTO_FILES diff --git a/test/go-tableau-loader/protoconf/loader/hub.pc.go b/test/go-tableau-loader/protoconf/loader/hub.pc.go index d34babe8..87aae258 100644 --- a/test/go-tableau-loader/protoconf/loader/hub.pc.go +++ b/test/go-tableau-loader/protoconf/loader/hub.pc.go @@ -211,36 +211,28 @@ func (h *Hub) GetLastLoadedTime() time.Time { return h.mc.Load().GetLastLoadedTime() } -func (h *Hub) GetHeroConf() *HeroConf { - return h.mc.Load().GetHeroConf() -} - -func (h *Hub) GetHeroBaseConf() *HeroBaseConf { - return h.mc.Load().GetHeroBaseConf() -} - -func (h *Hub) GetFruitConf() *FruitConf { - return h.mc.Load().GetFruitConf() +func (h *Hub) GetActivityConf() *ActivityConf { + return h.mc.Load().GetActivityConf() } -func (h *Hub) GetFruit2Conf() *Fruit2Conf { - return h.mc.Load().GetFruit2Conf() +func (h *Hub) GetChapterConf() *ChapterConf { + return h.mc.Load().GetChapterConf() } -func (h *Hub) GetFruit3Conf() *Fruit3Conf { - return h.mc.Load().GetFruit3Conf() +func (h *Hub) GetThemeConf() *ThemeConf { + return h.mc.Load().GetThemeConf() } -func (h *Hub) GetFruit4Conf() *Fruit4Conf { - return h.mc.Load().GetFruit4Conf() +func (h *Hub) GetTaskConf() *TaskConf { + return h.mc.Load().GetTaskConf() } -func (h *Hub) GetFruit5Conf() *Fruit5Conf { - return h.mc.Load().GetFruit5Conf() +func (h *Hub) GetHeroConf() *HeroConf { + return h.mc.Load().GetHeroConf() } -func (h *Hub) GetItemConf() *ItemConf { - return h.mc.Load().GetItemConf() +func (h *Hub) GetHeroBaseConf() *HeroBaseConf { + return h.mc.Load().GetHeroBaseConf() } func (h *Hub) GetPatchReplaceConf() *PatchReplaceConf { @@ -255,18 +247,26 @@ func (h *Hub) GetRecursivePatchConf() *RecursivePatchConf { return h.mc.Load().GetRecursivePatchConf() } -func (h *Hub) GetActivityConf() *ActivityConf { - return h.mc.Load().GetActivityConf() +func (h *Hub) GetFruitConf() *FruitConf { + return h.mc.Load().GetFruitConf() } -func (h *Hub) GetChapterConf() *ChapterConf { - return h.mc.Load().GetChapterConf() +func (h *Hub) GetFruit2Conf() *Fruit2Conf { + return h.mc.Load().GetFruit2Conf() } -func (h *Hub) GetThemeConf() *ThemeConf { - return h.mc.Load().GetThemeConf() +func (h *Hub) GetFruit3Conf() *Fruit3Conf { + return h.mc.Load().GetFruit3Conf() } -func (h *Hub) GetTaskConf() *TaskConf { - return h.mc.Load().GetTaskConf() +func (h *Hub) GetFruit4Conf() *Fruit4Conf { + return h.mc.Load().GetFruit4Conf() +} + +func (h *Hub) GetFruit5Conf() *Fruit5Conf { + return h.mc.Load().GetFruit5Conf() +} + +func (h *Hub) GetItemConf() *ItemConf { + return h.mc.Load().GetItemConf() } diff --git a/test/go-tableau-loader/protoconf/loader/messager_container.pc.go b/test/go-tableau-loader/protoconf/loader/messager_container.pc.go index bb7fc8b5..df21de5c 100644 --- a/test/go-tableau-loader/protoconf/loader/messager_container.pc.go +++ b/test/go-tableau-loader/protoconf/loader/messager_container.pc.go @@ -13,42 +13,42 @@ type MessagerContainer struct { messagerMap MessagerMap loadedTime time.Time // all messagers as fields for fast access + activityConf *ActivityConf + chapterConf *ChapterConf + themeConf *ThemeConf + taskConf *TaskConf heroConf *HeroConf heroBaseConf *HeroBaseConf + patchReplaceConf *PatchReplaceConf + patchMergeConf *PatchMergeConf + recursivePatchConf *RecursivePatchConf fruitConf *FruitConf fruit2Conf *Fruit2Conf fruit3Conf *Fruit3Conf fruit4Conf *Fruit4Conf fruit5Conf *Fruit5Conf itemConf *ItemConf - patchReplaceConf *PatchReplaceConf - patchMergeConf *PatchMergeConf - recursivePatchConf *RecursivePatchConf - activityConf *ActivityConf - chapterConf *ChapterConf - themeConf *ThemeConf - taskConf *TaskConf } func newMessagerContainer(messagerMap MessagerMap) *MessagerContainer { return &MessagerContainer{ messagerMap: messagerMap, loadedTime: time.Now(), + activityConf: GetMessager[*ActivityConf](messagerMap), + chapterConf: GetMessager[*ChapterConf](messagerMap), + themeConf: GetMessager[*ThemeConf](messagerMap), + taskConf: GetMessager[*TaskConf](messagerMap), heroConf: GetMessager[*HeroConf](messagerMap), heroBaseConf: GetMessager[*HeroBaseConf](messagerMap), + patchReplaceConf: GetMessager[*PatchReplaceConf](messagerMap), + patchMergeConf: GetMessager[*PatchMergeConf](messagerMap), + recursivePatchConf: GetMessager[*RecursivePatchConf](messagerMap), fruitConf: GetMessager[*FruitConf](messagerMap), fruit2Conf: GetMessager[*Fruit2Conf](messagerMap), fruit3Conf: GetMessager[*Fruit3Conf](messagerMap), fruit4Conf: GetMessager[*Fruit4Conf](messagerMap), fruit5Conf: GetMessager[*Fruit5Conf](messagerMap), itemConf: GetMessager[*ItemConf](messagerMap), - patchReplaceConf: GetMessager[*PatchReplaceConf](messagerMap), - patchMergeConf: GetMessager[*PatchMergeConf](messagerMap), - recursivePatchConf: GetMessager[*RecursivePatchConf](messagerMap), - activityConf: GetMessager[*ActivityConf](messagerMap), - chapterConf: GetMessager[*ChapterConf](messagerMap), - themeConf: GetMessager[*ThemeConf](messagerMap), - taskConf: GetMessager[*TaskConf](messagerMap), } } @@ -66,36 +66,28 @@ func (mc *MessagerContainer) GetLastLoadedTime() time.Time { // Auto-generated getters below -func (mc *MessagerContainer) GetHeroConf() *HeroConf { - return mc.heroConf -} - -func (mc *MessagerContainer) GetHeroBaseConf() *HeroBaseConf { - return mc.heroBaseConf -} - -func (mc *MessagerContainer) GetFruitConf() *FruitConf { - return mc.fruitConf +func (mc *MessagerContainer) GetActivityConf() *ActivityConf { + return mc.activityConf } -func (mc *MessagerContainer) GetFruit2Conf() *Fruit2Conf { - return mc.fruit2Conf +func (mc *MessagerContainer) GetChapterConf() *ChapterConf { + return mc.chapterConf } -func (mc *MessagerContainer) GetFruit3Conf() *Fruit3Conf { - return mc.fruit3Conf +func (mc *MessagerContainer) GetThemeConf() *ThemeConf { + return mc.themeConf } -func (mc *MessagerContainer) GetFruit4Conf() *Fruit4Conf { - return mc.fruit4Conf +func (mc *MessagerContainer) GetTaskConf() *TaskConf { + return mc.taskConf } -func (mc *MessagerContainer) GetFruit5Conf() *Fruit5Conf { - return mc.fruit5Conf +func (mc *MessagerContainer) GetHeroConf() *HeroConf { + return mc.heroConf } -func (mc *MessagerContainer) GetItemConf() *ItemConf { - return mc.itemConf +func (mc *MessagerContainer) GetHeroBaseConf() *HeroBaseConf { + return mc.heroBaseConf } func (mc *MessagerContainer) GetPatchReplaceConf() *PatchReplaceConf { @@ -110,18 +102,26 @@ func (mc *MessagerContainer) GetRecursivePatchConf() *RecursivePatchConf { return mc.recursivePatchConf } -func (mc *MessagerContainer) GetActivityConf() *ActivityConf { - return mc.activityConf +func (mc *MessagerContainer) GetFruitConf() *FruitConf { + return mc.fruitConf } -func (mc *MessagerContainer) GetChapterConf() *ChapterConf { - return mc.chapterConf +func (mc *MessagerContainer) GetFruit2Conf() *Fruit2Conf { + return mc.fruit2Conf } -func (mc *MessagerContainer) GetThemeConf() *ThemeConf { - return mc.themeConf +func (mc *MessagerContainer) GetFruit3Conf() *Fruit3Conf { + return mc.fruit3Conf } -func (mc *MessagerContainer) GetTaskConf() *TaskConf { - return mc.taskConf +func (mc *MessagerContainer) GetFruit4Conf() *Fruit4Conf { + return mc.fruit4Conf +} + +func (mc *MessagerContainer) GetFruit5Conf() *Fruit5Conf { + return mc.fruit5Conf +} + +func (mc *MessagerContainer) GetItemConf() *ItemConf { + return mc.itemConf } From 458c1e659cafec2536e3e2e333c45acfc985251b Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 12 Mar 2026 17:03:44 +0800 Subject: [PATCH 11/32] test: add test case for Fruit3Conf index structure and level message generation --- internal/index/descriptor_test.go | 95 +++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/internal/index/descriptor_test.go b/internal/index/descriptor_test.go index 56d5a88c..94cae9b7 100644 --- a/internal/index/descriptor_test.go +++ b/internal/index/descriptor_test.go @@ -589,6 +589,101 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, }, }, + { + // Fruit3Conf: list -> list -> map -> list + // indexes at Country level (L2) and Attr level (L4), + // ordered index at Item level (L3). + name: "Fruit3Conf", + args: args{ + md: md[*protoconf.Fruit3Conf](), + }, + want: &IndexDescriptor{ + LevelMessage: &LevelMessage{ + Depth: 0, + MapDepth: 0, + FD: fd[*protoconf.Fruit3Conf]("fruit_list"), + NextLevel: &LevelMessage{ + Depth: 1, + MapDepth: 0, + FD: fd[*protoconf.Fruit3Conf_Fruit]("country_list"), + NextLevel: &LevelMessage{ + Depth: 2, + MapDepth: 0, + FD: fd[*protoconf.Fruit3Conf_Fruit_Country]("item_map"), + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"CountryName"}, + Name: "", + }, + MD: md[*protoconf.Fruit3Conf_Fruit_Country](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Fruit3Conf_Fruit_Country]("name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Fruit3Conf_Fruit_Country]("name"), + }, + }, + }, + }, + }, + NextLevel: &LevelMessage{ + Depth: 3, + MapDepth: 1, + FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("attr_list"), + OrderedIndexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"CountryItemPrice"}, + SortedCols: []string{"CountryItemID"}, + Name: "", + }, + MD: md[*protoconf.Fruit3Conf_Fruit_Country_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("price"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("price"), + }, + }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("id"), + }, + }, + }, + }, + }, + NextLevel: &LevelMessage{ + Depth: 4, + MapDepth: 1, + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"CountryItemAttrName"}, + Name: "", + }, + MD: md[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr]("name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr]("name"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, { // Fruit5Conf: 3-level map (fruit_map -> country_map -> item_map), // but indexes only at MapDepth=2 (Country level). From 4a69d75979177f3d42a37ff712d9737a589e0c4b Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 17:06:25 +0800 Subject: [PATCH 12/32] ci: add testing-cpp workflow --- .github/workflows/testing-cpp.yml | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/testing-cpp.yml diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml new file mode 100644 index 00000000..5b90a04c --- /dev/null +++ b/.github/workflows/testing-cpp.yml @@ -0,0 +1,63 @@ +name: Testing C++ + +# Trigger on pushes, PRs (excluding documentation changes), and nightly. +on: + push: + branches: [master, main] + pull_request: + schedule: + - cron: 0 0 * * * # daily at 00:00 + workflow_dispatch: + +permissions: + contents: read + +jobs: + test: + strategy: + matrix: + include: + - os: ubuntu-latest + init_script: bash init.sh + gen_script: bash test/cpp-tableau-loader/gen.sh + cmake_config: cmake -S test/cpp-tableau-loader -B test/cpp-tableau-loader/build + run_loader: ./test/cpp-tableau-loader/bin/loader + - os: windows-latest + init_script: .\init.bat + gen_script: test\cpp-tableau-loader\gen.bat + cmake_config: cmake -S test/cpp-tableau-loader -B test/cpp-tableau-loader/build -G "NMake Makefiles" + run_loader: .\test\cpp-tableau-loader\bin\loader.exe + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: 1.21.x + cache: true + + - name: Install CMake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: "3.22.x" + + - name: Init protobuf + shell: bash + run: ${{ matrix.init_script }} + + - name: Generate protoconf + shell: bash + run: ${{ matrix.gen_script }} + + - name: CMake Configure + run: ${{ matrix.cmake_config }} + + - name: CMake Build + run: cmake --build test/cpp-tableau-loader/build --parallel + + - name: Run loader + run: ${{ matrix.run_loader }} From c6c90db954f3af5e5fe4760707c9093ad31a0c6d Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 12 Mar 2026 17:32:02 +0800 Subject: [PATCH 13/32] refactor: standardize formatting in index test cases --- internal/index/index_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/index/index_test.go b/internal/index/index_test.go index f0f58a07..e6c3ce8e 100644 --- a/internal/index/index_test.go +++ b/internal/index/index_test.go @@ -15,18 +15,18 @@ func Test_parseIndex(t *testing.T) { name: "Single column with single sorted column and name", input: "Column1@IndexName", want: &Index{ - Cols: []string{"Column1"}, + Cols: []string{"Column1"}, SortedCols: []string{"SortedCol1"}, - Name: "IndexName", + Name: "IndexName", }, }, { name: "Multi-column with multi sorted column and name", input: "( Column1 , Column2 )< SortedCol1 , SortedCol2 >@IndexName", want: &Index{ - Cols: []string{"Column1", "Column2"}, + Cols: []string{"Column1", "Column2"}, SortedCols: []string{"SortedCol1", "SortedCol2"}, - Name: "IndexName", + Name: "IndexName", }, }, { @@ -48,15 +48,15 @@ func Test_parseIndex(t *testing.T) { name: "Single column with single sorted column only", input: "Column6", want: &Index{ - Cols: []string{"Column6"}, + Cols: []string{"Column6"}, SortedCols: []string{"SortedCol"}, }, }, { - name: "zinotest", + name: "Single-column with multi sorted columns", input: "ActivityID", want: &Index{ - Cols: []string{"ActivityID"}, + Cols: []string{"ActivityID"}, SortedCols: []string{"Goal", "ID"}, }, }, @@ -64,9 +64,9 @@ func Test_parseIndex(t *testing.T) { name: "Multi-column with spaces around commas", input: "(Column7, Column8, Column9)@IndexName", want: &Index{ - Cols: []string{"Column7", "Column8", "Column9"}, + Cols: []string{"Column7", "Column8", "Column9"}, SortedCols: []string{"SortedCol7", "SortedCol8", "SortedCol9"}, - Name: "IndexName", + Name: "IndexName", }, }, { From fbf35d23c61fa41839edef4e5ccba1c39c99ee08 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 17:38:43 +0800 Subject: [PATCH 14/32] ci: fix testing-cpp workflow - cmake version and working directory --- .github/workflows/testing-cpp.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index 5b90a04c..04cf7821 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -21,12 +21,12 @@ jobs: init_script: bash init.sh gen_script: bash test/cpp-tableau-loader/gen.sh cmake_config: cmake -S test/cpp-tableau-loader -B test/cpp-tableau-loader/build - run_loader: ./test/cpp-tableau-loader/bin/loader + run_loader: ./bin/loader - os: windows-latest init_script: .\init.bat gen_script: test\cpp-tableau-loader\gen.bat cmake_config: cmake -S test/cpp-tableau-loader -B test/cpp-tableau-loader/build -G "NMake Makefiles" - run_loader: .\test\cpp-tableau-loader\bin\loader.exe + run_loader: .\bin\loader.exe runs-on: ${{ matrix.os }} @@ -43,7 +43,7 @@ jobs: - name: Install CMake uses: jwlawson/actions-setup-cmake@v2 with: - cmake-version: "3.22.x" + cmake-version: "3.x" - name: Init protobuf shell: bash @@ -60,4 +60,5 @@ jobs: run: cmake --build test/cpp-tableau-loader/build --parallel - name: Run loader + working-directory: test/cpp-tableau-loader run: ${{ matrix.run_loader }} From 5ebf5f16d3e41705a331c1a818c8a65bd6537e8e Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 17:43:32 +0800 Subject: [PATCH 15/32] ci: fix Windows init/gen scripts - use cmd /c to run .bat files in bash shell --- .github/workflows/testing-cpp.yml | 4 ++-- .github/workflows/testing-go.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index 04cf7821..6d56b4d9 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -23,8 +23,8 @@ jobs: cmake_config: cmake -S test/cpp-tableau-loader -B test/cpp-tableau-loader/build run_loader: ./bin/loader - os: windows-latest - init_script: .\init.bat - gen_script: test\cpp-tableau-loader\gen.bat + init_script: cmd /c init.bat + gen_script: cmd /c test\cpp-tableau-loader\gen.bat cmake_config: cmake -S test/cpp-tableau-loader -B test/cpp-tableau-loader/build -G "NMake Makefiles" run_loader: .\bin\loader.exe diff --git a/.github/workflows/testing-go.yml b/.github/workflows/testing-go.yml index 25de3304..9b661952 100644 --- a/.github/workflows/testing-go.yml +++ b/.github/workflows/testing-go.yml @@ -16,7 +16,7 @@ jobs: test: strategy: matrix: - go-version: [1.21.x, 1.23.x] + go-version: [1.21.x] os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} From 6c9cafc5681e20819fe83cdf7e0288cd2916a558 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 17:47:28 +0800 Subject: [PATCH 16/32] ci: setup MSVC environment on Windows for NMake Makefiles --- .github/workflows/testing-cpp.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index 6d56b4d9..6c266a91 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -45,6 +45,10 @@ jobs: with: cmake-version: "3.x" + - name: Setup MSVC (Windows only) + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + - name: Init protobuf shell: bash run: ${{ matrix.init_script }} From e86c7dcb5d50c6636c9eeb2cc7b5bb6887bae976 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 17:49:54 +0800 Subject: [PATCH 17/32] ci: fix non-existent action versions v6 in testing-go.yml; use --parallel in init.sh --- .github/workflows/testing-go.yml | 4 ++-- init.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing-go.yml b/.github/workflows/testing-go.yml index 9b661952..f674e251 100644 --- a/.github/workflows/testing-go.yml +++ b/.github/workflows/testing-go.yml @@ -23,10 +23,10 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Install Go - uses: actions/setup-go@v6 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} cache: true diff --git a/init.sh b/init.sh index 5e78d447..4844fb2e 100755 --- a/init.sh +++ b/init.sh @@ -26,4 +26,4 @@ cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug # Compile the code -cmake --build build -j16 +cmake --build build --parallel From 0ee1b767a5d11a9dc4678aaa1f58eee08f025860 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 12 Mar 2026 17:58:44 +0800 Subject: [PATCH 18/32] refactor: remove unused configuration getters and streamline MessagerContainer structure --- .../protoconf/loader/hub.pc.go | 44 ++++++------ .../protoconf/loader/messager_container.pc.go | 68 +++++++++---------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/test/go-tableau-loader/protoconf/loader/hub.pc.go b/test/go-tableau-loader/protoconf/loader/hub.pc.go index 87aae258..e3b5db11 100644 --- a/test/go-tableau-loader/protoconf/loader/hub.pc.go +++ b/test/go-tableau-loader/protoconf/loader/hub.pc.go @@ -211,6 +211,10 @@ func (h *Hub) GetLastLoadedTime() time.Time { return h.mc.Load().GetLastLoadedTime() } +func (h *Hub) GetItemConf() *ItemConf { + return h.mc.Load().GetItemConf() +} + func (h *Hub) GetActivityConf() *ActivityConf { return h.mc.Load().GetActivityConf() } @@ -227,26 +231,6 @@ func (h *Hub) GetTaskConf() *TaskConf { return h.mc.Load().GetTaskConf() } -func (h *Hub) GetHeroConf() *HeroConf { - return h.mc.Load().GetHeroConf() -} - -func (h *Hub) GetHeroBaseConf() *HeroBaseConf { - return h.mc.Load().GetHeroBaseConf() -} - -func (h *Hub) GetPatchReplaceConf() *PatchReplaceConf { - return h.mc.Load().GetPatchReplaceConf() -} - -func (h *Hub) GetPatchMergeConf() *PatchMergeConf { - return h.mc.Load().GetPatchMergeConf() -} - -func (h *Hub) GetRecursivePatchConf() *RecursivePatchConf { - return h.mc.Load().GetRecursivePatchConf() -} - func (h *Hub) GetFruitConf() *FruitConf { return h.mc.Load().GetFruitConf() } @@ -267,6 +251,22 @@ func (h *Hub) GetFruit5Conf() *Fruit5Conf { return h.mc.Load().GetFruit5Conf() } -func (h *Hub) GetItemConf() *ItemConf { - return h.mc.Load().GetItemConf() +func (h *Hub) GetHeroConf() *HeroConf { + return h.mc.Load().GetHeroConf() +} + +func (h *Hub) GetHeroBaseConf() *HeroBaseConf { + return h.mc.Load().GetHeroBaseConf() +} + +func (h *Hub) GetPatchReplaceConf() *PatchReplaceConf { + return h.mc.Load().GetPatchReplaceConf() +} + +func (h *Hub) GetPatchMergeConf() *PatchMergeConf { + return h.mc.Load().GetPatchMergeConf() +} + +func (h *Hub) GetRecursivePatchConf() *RecursivePatchConf { + return h.mc.Load().GetRecursivePatchConf() } diff --git a/test/go-tableau-loader/protoconf/loader/messager_container.pc.go b/test/go-tableau-loader/protoconf/loader/messager_container.pc.go index df21de5c..8ba64d0a 100644 --- a/test/go-tableau-loader/protoconf/loader/messager_container.pc.go +++ b/test/go-tableau-loader/protoconf/loader/messager_container.pc.go @@ -13,42 +13,42 @@ type MessagerContainer struct { messagerMap MessagerMap loadedTime time.Time // all messagers as fields for fast access + itemConf *ItemConf activityConf *ActivityConf chapterConf *ChapterConf themeConf *ThemeConf taskConf *TaskConf - heroConf *HeroConf - heroBaseConf *HeroBaseConf - patchReplaceConf *PatchReplaceConf - patchMergeConf *PatchMergeConf - recursivePatchConf *RecursivePatchConf fruitConf *FruitConf fruit2Conf *Fruit2Conf fruit3Conf *Fruit3Conf fruit4Conf *Fruit4Conf fruit5Conf *Fruit5Conf - itemConf *ItemConf + heroConf *HeroConf + heroBaseConf *HeroBaseConf + patchReplaceConf *PatchReplaceConf + patchMergeConf *PatchMergeConf + recursivePatchConf *RecursivePatchConf } func newMessagerContainer(messagerMap MessagerMap) *MessagerContainer { return &MessagerContainer{ messagerMap: messagerMap, loadedTime: time.Now(), + itemConf: GetMessager[*ItemConf](messagerMap), activityConf: GetMessager[*ActivityConf](messagerMap), chapterConf: GetMessager[*ChapterConf](messagerMap), themeConf: GetMessager[*ThemeConf](messagerMap), taskConf: GetMessager[*TaskConf](messagerMap), - heroConf: GetMessager[*HeroConf](messagerMap), - heroBaseConf: GetMessager[*HeroBaseConf](messagerMap), - patchReplaceConf: GetMessager[*PatchReplaceConf](messagerMap), - patchMergeConf: GetMessager[*PatchMergeConf](messagerMap), - recursivePatchConf: GetMessager[*RecursivePatchConf](messagerMap), fruitConf: GetMessager[*FruitConf](messagerMap), fruit2Conf: GetMessager[*Fruit2Conf](messagerMap), fruit3Conf: GetMessager[*Fruit3Conf](messagerMap), fruit4Conf: GetMessager[*Fruit4Conf](messagerMap), fruit5Conf: GetMessager[*Fruit5Conf](messagerMap), - itemConf: GetMessager[*ItemConf](messagerMap), + heroConf: GetMessager[*HeroConf](messagerMap), + heroBaseConf: GetMessager[*HeroBaseConf](messagerMap), + patchReplaceConf: GetMessager[*PatchReplaceConf](messagerMap), + patchMergeConf: GetMessager[*PatchMergeConf](messagerMap), + recursivePatchConf: GetMessager[*RecursivePatchConf](messagerMap), } } @@ -66,6 +66,10 @@ func (mc *MessagerContainer) GetLastLoadedTime() time.Time { // Auto-generated getters below +func (mc *MessagerContainer) GetItemConf() *ItemConf { + return mc.itemConf +} + func (mc *MessagerContainer) GetActivityConf() *ActivityConf { return mc.activityConf } @@ -82,26 +86,6 @@ func (mc *MessagerContainer) GetTaskConf() *TaskConf { return mc.taskConf } -func (mc *MessagerContainer) GetHeroConf() *HeroConf { - return mc.heroConf -} - -func (mc *MessagerContainer) GetHeroBaseConf() *HeroBaseConf { - return mc.heroBaseConf -} - -func (mc *MessagerContainer) GetPatchReplaceConf() *PatchReplaceConf { - return mc.patchReplaceConf -} - -func (mc *MessagerContainer) GetPatchMergeConf() *PatchMergeConf { - return mc.patchMergeConf -} - -func (mc *MessagerContainer) GetRecursivePatchConf() *RecursivePatchConf { - return mc.recursivePatchConf -} - func (mc *MessagerContainer) GetFruitConf() *FruitConf { return mc.fruitConf } @@ -122,6 +106,22 @@ func (mc *MessagerContainer) GetFruit5Conf() *Fruit5Conf { return mc.fruit5Conf } -func (mc *MessagerContainer) GetItemConf() *ItemConf { - return mc.itemConf +func (mc *MessagerContainer) GetHeroConf() *HeroConf { + return mc.heroConf +} + +func (mc *MessagerContainer) GetHeroBaseConf() *HeroBaseConf { + return mc.heroBaseConf +} + +func (mc *MessagerContainer) GetPatchReplaceConf() *PatchReplaceConf { + return mc.patchReplaceConf +} + +func (mc *MessagerContainer) GetPatchMergeConf() *PatchMergeConf { + return mc.patchMergeConf +} + +func (mc *MessagerContainer) GetRecursivePatchConf() *RecursivePatchConf { + return mc.recursivePatchConf } From c22d64d15313ab96f4dce69f26c595e48be2492b Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 18:19:11 +0800 Subject: [PATCH 19/32] ci: fix cpp testing workflow for Windows compatibility - Remove shell: bash from Init/Generate steps to use default cmd on Windows - Add working-directory to CMake Configure step - Fix gen_script paths relative to working-directory --- .github/workflows/testing-cpp.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index 6c266a91..02a9434f 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -19,13 +19,13 @@ jobs: include: - os: ubuntu-latest init_script: bash init.sh - gen_script: bash test/cpp-tableau-loader/gen.sh - cmake_config: cmake -S test/cpp-tableau-loader -B test/cpp-tableau-loader/build + gen_script: bash gen.sh + cmake_config: cmake -S . -B build run_loader: ./bin/loader - os: windows-latest init_script: cmd /c init.bat - gen_script: cmd /c test\cpp-tableau-loader\gen.bat - cmake_config: cmake -S test/cpp-tableau-loader -B test/cpp-tableau-loader/build -G "NMake Makefiles" + gen_script: cmd /c gen.bat + cmake_config: cmake -S . -B build -G "NMake Makefiles" run_loader: .\bin\loader.exe runs-on: ${{ matrix.os }} @@ -50,18 +50,19 @@ jobs: uses: ilammy/msvc-dev-cmd@v1 - name: Init protobuf - shell: bash run: ${{ matrix.init_script }} - name: Generate protoconf - shell: bash + working-directory: test/cpp-tableau-loader run: ${{ matrix.gen_script }} - name: CMake Configure + working-directory: test/cpp-tableau-loader run: ${{ matrix.cmake_config }} - name: CMake Build - run: cmake --build test/cpp-tableau-loader/build --parallel + working-directory: test/cpp-tableau-loader + run: cmake --build build --parallel - name: Run loader working-directory: test/cpp-tableau-loader From 4ac97124311859d484486bd4414be08ae6e28b5f Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 19:26:35 +0800 Subject: [PATCH 20/32] chore: update workflow actions --- .github/workflows/testing-go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testing-go.yml b/.github/workflows/testing-go.yml index f674e251..9b661952 100644 --- a/.github/workflows/testing-go.yml +++ b/.github/workflows/testing-go.yml @@ -23,10 +23,10 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: ${{ matrix.go-version }} cache: true From 6eb147e1253b688ec2a3c44079c06f0252a66d1c Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 19:39:29 +0800 Subject: [PATCH 21/32] ci: remove redundant Ninja installation step on Windows --- .github/workflows/testing-cpp.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index 02a9434f..cc220d47 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -25,10 +25,11 @@ jobs: - os: windows-latest init_script: cmd /c init.bat gen_script: cmd /c gen.bat - cmake_config: cmake -S . -B build -G "NMake Makefiles" + cmake_config: cmake -S . -B build -G "Ninja" run_loader: .\bin\loader.exe runs-on: ${{ matrix.os }} + timeout-minutes: 30 steps: - name: Checkout Code @@ -50,6 +51,7 @@ jobs: uses: ilammy/msvc-dev-cmd@v1 - name: Init protobuf + timeout-minutes: 20 run: ${{ matrix.init_script }} - name: Generate protoconf From 1466e1ef923cecb8b00d213373b481d62cd3630e Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 20:41:24 +0800 Subject: [PATCH 22/32] Improve C++ test workflow and build scripts - Fix testing-cpp.yml: skip CMake setup action on Linux, install cmake via apt instead - Improve cpp-tableau-loader gen.sh and gen.bat scripts - Update go-tableau-loader gen.sh and gen.bat scripts - Update CMakeLists.txt and init.bat --- .github/workflows/testing-cpp.yml | 18 ++++++--- README.md | 4 +- init.bat | 4 +- test/cpp-tableau-loader/CMakeLists.txt | 52 ++++++++++---------------- test/cpp-tableau-loader/gen.bat | 31 ++++++++++++++- test/cpp-tableau-loader/gen.sh | 27 ++++++++++++- test/go-tableau-loader/gen.bat | 31 ++++++++++++++- test/go-tableau-loader/gen.sh | 26 +++++++++++-- 8 files changed, 141 insertions(+), 52 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index cc220d47..a9d578bc 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -18,12 +18,10 @@ jobs: matrix: include: - os: ubuntu-latest - init_script: bash init.sh gen_script: bash gen.sh cmake_config: cmake -S . -B build run_loader: ./bin/loader - os: windows-latest - init_script: cmd /c init.bat gen_script: cmd /c gen.bat cmake_config: cmake -S . -B build -G "Ninja" run_loader: .\bin\loader.exe @@ -41,18 +39,26 @@ jobs: go-version: 1.21.x cache: true - - name: Install CMake + - name: Install CMake (Windows) + if: runner.os == 'Windows' uses: jwlawson/actions-setup-cmake@v2 with: cmake-version: "3.x" + - name: Install dependencies (Ubuntu) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y cmake libprotobuf-dev protobuf-compiler + - name: Setup MSVC (Windows only) if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 - - name: Init protobuf - timeout-minutes: 20 - run: ${{ matrix.init_script }} + - name: Install protobuf (Windows) + if: runner.os == 'Windows' + run: choco install protoc + + - name: Init submodules + run: git submodule update --init --recursive - name: Generate protoconf working-directory: test/cpp-tableau-loader diff --git a/README.md b/README.md index 45baa4a3..4ff8e00d 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The official config loader for [Tableau](https://github.com/tableauio/tableau). - C++17: `cmake -S . -B build` - C++20: `cmake -S . -B build -DCMAKE_CXX_STANDARD=20` - clang: `cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++` -- Build: `cmake --build build -j16` +- Build: `cmake --build build --parallel` - Run: `./bin/loader` ### Dev at Windows @@ -44,7 +44,7 @@ The official config loader for [Tableau](https://github.com/tableauio/tableau). - CMake: - C++17: `cmake -S . -B build -G "NMake Makefiles"` - C++20: `cmake -S . -B build -G "NMake Makefiles" -DCMAKE_CXX_STANDARD=20` -- Build: `cmake --build build` +- Build: `cmake --build build --parallel` - Run: `.\bin\loader.exe` ### References diff --git a/init.bat b/init.bat index d97e1ab8..f1921aa4 100644 --- a/init.bat +++ b/init.bat @@ -16,11 +16,11 @@ REM Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README. cd cmake REM use Debug version cmake -S . -B build ^ --G "NMake Makefiles" ^ +-G "Ninja" ^ -DCMAKE_BUILD_TYPE=Debug ^ -DCMAKE_POLICY_VERSION_MINIMUM="3.5" REM Compile the code -cmake --build build +cmake --build build --parallel endlocal diff --git a/test/cpp-tableau-loader/CMakeLists.txt b/test/cpp-tableau-loader/CMakeLists.txt index 7787aed9..fb811ac2 100644 --- a/test/cpp-tableau-loader/CMakeLists.txt +++ b/test/cpp-tableau-loader/CMakeLists.txt @@ -23,45 +23,31 @@ else() SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -fPIC -Wno-deprecated -Wno-unused-variable -Wno-sign-compare -Wno-strict-aliasing -fno-strict-aliasing -DNDEBUG") endif() -# root dir define -SET(CMAKE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -SET(THRID_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../third_party) - # google protobuf -SET(PROTOBUF_ROOT_DIR ${THRID_PARTY_DIR}/_submodules/protobuf/) -SET(PROTOBUF_SRC_DIR ${PROTOBUF_ROOT_DIR}/src/) -SET(PROTOBUF_BUILD_DIR ${PROTOBUF_ROOT_DIR}/cmake/build/) +# Try to find protobuf from local submodule first, then fallback to system. +set(LOCAL_PROTOBUF_BUILD_DIR "${PROJECT_SOURCE_DIR}/../../third_party/_submodules/protobuf/cmake/build") +set(LOCAL_PROTOBUF_SRC_DIR "${PROJECT_SOURCE_DIR}/../../third_party/_submodules/protobuf/src") +if(EXISTS "${LOCAL_PROTOBUF_BUILD_DIR}" AND EXISTS "${LOCAL_PROTOBUF_SRC_DIR}") + message(STATUS "Found local protobuf submodule, using it preferentially.") + list(PREPEND CMAKE_PREFIX_PATH "${LOCAL_PROTOBUF_BUILD_DIR}") + set(Protobuf_INCLUDE_DIR "${LOCAL_PROTOBUF_SRC_DIR}" CACHE PATH "Protobuf include dir" FORCE) +endif() +find_package(Protobuf REQUIRED) +message(STATUS "Using protobuf ${Protobuf_VERSION}") + # loader SET(LOADER_SRC_DIR ${PROJECT_SOURCE_DIR}/src/) -# protoconf -SET(PROTOCONF_DIR ${TOOLS_DIR}/protoconf) -SET(PROTOCONF_TOOL ${PROTOCONF_DIR}/protoconf) - -# common include -SET(COMMON_INCLUDE_DIR - ${PROTOBUF_SRC_DIR} - ${LOADER_SRC_DIR} - ) - -# common lib -if(MSVC) - SET(COMMON_LIB - ${PROTOBUF_BUILD_DIR}/libprotobufd.lib - ) - SET(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebug") -else() - SET(COMMON_LIB - ${PROTOBUF_BUILD_DIR}/libprotobufd.a - pthread - stdc++fs - ) -endif() - # include -include_directories(${COMMON_INCLUDE_DIR} ${LOADER_SRC_DIR}/protoconf) +include_directories(${Protobuf_INCLUDE_DIRS} ${LOADER_SRC_DIR} ${LOADER_SRC_DIR}/protoconf) # add the executable add_executable(${PROJECT_NAME} ${PROTO_SOURCE} ${SOURCE}) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) -target_link_libraries(${PROJECT_NAME} ${COMMON_LIB}) + +# link libraries +if(MSVC) + target_link_libraries(${PROJECT_NAME} protobuf::libprotobuf) +else() + target_link_libraries(${PROJECT_NAME} protobuf::libprotobuf pthread stdc++fs) +endif() diff --git a/test/cpp-tableau-loader/gen.bat b/test/cpp-tableau-loader/gen.bat index c1a4ac96..1751c14c 100644 --- a/test/cpp-tableau-loader/gen.bat +++ b/test/cpp-tableau-loader/gen.bat @@ -5,8 +5,35 @@ setlocal enabledelayedexpansion for /f "delims=" %%i in ('git rev-parse --show-toplevel') do set repoRoot=%%i cd /d "%repoRoot%" -set "PROTOC=%repoRoot%\third_party\_submodules\protobuf\cmake\build\protoc.exe" -set "PROTOBUF_PROTO=%repoRoot%\third_party\_submodules\protobuf\src" +REM Allow overriding protoc via environment variable. +REM Default to locally compiled protoc for local development; fallback to system protoc. +if not defined PROTOC ( + if exist "%repoRoot%\third_party\_submodules\protobuf\cmake\build\protoc.exe" ( + set "PROTOC=%repoRoot%\third_party\_submodules\protobuf\cmake\build\protoc.exe" + ) else ( + where protoc >nul 2>nul + if !errorlevel! equ 0 ( + for /f "delims=" %%p in ('where protoc') do set "PROTOC=%%p" + ) else ( + echo Error: protoc not found. Please build protobuf submodule or install protoc. >&2 + exit /b 1 + ) + ) +) +REM Allow overriding protobuf include path via environment variable. +REM Default to local submodule source; fallback to system include path. +if not defined PROTOBUF_PROTO ( + if exist "%repoRoot%\third_party\_submodules\protobuf\src\google\protobuf" ( + set "PROTOBUF_PROTO=%repoRoot%\third_party\_submodules\protobuf\src" + ) else ( + for /f "delims=" %%p in ('where protoc 2^>nul') do set "_PROTOC_DIR=%%~dpp" + if defined _PROTOC_DIR ( + set "PROTOBUF_PROTO=!_PROTOC_DIR!..\include" + ) else ( + set "PROTOBUF_PROTO=%repoRoot%\third_party\_submodules\protobuf\src" + ) + ) +) set "TABLEAU_PROTO=%repoRoot%\third_party\_submodules\tableau\proto" set "ROOTDIR=%repoRoot%\test\cpp-tableau-loader" set "PLGUIN_DIR=%repoRoot%\cmd\protoc-gen-cpp-tableau-loader" diff --git a/test/cpp-tableau-loader/gen.sh b/test/cpp-tableau-loader/gen.sh index 32756cb3..017757e3 100755 --- a/test/cpp-tableau-loader/gen.sh +++ b/test/cpp-tableau-loader/gen.sh @@ -7,8 +7,31 @@ set -o pipefail shopt -s globstar cd "$(git rev-parse --show-toplevel)" -PROTOC="./third_party/_submodules/protobuf/cmake/build/protoc" -PROTOBUF_PROTO="./third_party/_submodules/protobuf/src" + +# Allow overriding protoc via environment variable. +# Default to locally compiled protoc for local development; fallback to system protoc. +LOCAL_PROTOC="./third_party/_submodules/protobuf/cmake/build/protoc" +if [ -z "$PROTOC" ]; then + if [ -x "$LOCAL_PROTOC" ]; then + PROTOC="$LOCAL_PROTOC" + else + PROTOC="$(which protoc 2>/dev/null || true)" + fi +fi +if [ -z "$PROTOC" ]; then + echo "Error: protoc not found. Please build protobuf submodule or install protoc." >&2 + exit 1 +fi +# Allow overriding protobuf include path via environment variable. +# Default to local submodule source; fallback to system include path. +LOCAL_PROTOBUF_PROTO="./third_party/_submodules/protobuf/src" +if [ -z "$PROTOBUF_PROTO" ]; then + if [ -d "$LOCAL_PROTOBUF_PROTO/google/protobuf" ]; then + PROTOBUF_PROTO="$LOCAL_PROTOBUF_PROTO" + else + PROTOBUF_PROTO="$(pkg-config --variable=includedir protobuf 2>/dev/null || echo /usr/include)" + fi +fi TABLEAU_PROTO="./third_party/_submodules/tableau/proto" ROOTDIR="./test/cpp-tableau-loader" PLGUIN_DIR="./cmd/protoc-gen-cpp-tableau-loader" diff --git a/test/go-tableau-loader/gen.bat b/test/go-tableau-loader/gen.bat index 7a9c2fb3..2520760e 100644 --- a/test/go-tableau-loader/gen.bat +++ b/test/go-tableau-loader/gen.bat @@ -5,8 +5,35 @@ setlocal enabledelayedexpansion for /f "delims=" %%i in ('git rev-parse --show-toplevel') do set repoRoot=%%i cd /d "%repoRoot%" -set "PROTOC=%repoRoot%\third_party\_submodules\protobuf\cmake\build\protoc.exe" -set "PROTOBUF_PROTO=%repoRoot%\third_party\_submodules\protobuf\src" +REM Allow overriding protoc via environment variable. +REM Default to locally compiled protoc for local development; fallback to system protoc. +if not defined PROTOC ( + if exist "%repoRoot%\third_party\_submodules\protobuf\cmake\build\protoc.exe" ( + set "PROTOC=%repoRoot%\third_party\_submodules\protobuf\cmake\build\protoc.exe" + ) else ( + where protoc >nul 2>nul + if !errorlevel! equ 0 ( + for /f "delims=" %%p in ('where protoc') do set "PROTOC=%%p" + ) else ( + echo Error: protoc not found. Please build protobuf submodule or install protoc. >&2 + exit /b 1 + ) + ) +) +REM Allow overriding protobuf include path via environment variable. +REM Default to local submodule source; fallback to system include path. +if not defined PROTOBUF_PROTO ( + if exist "%repoRoot%\third_party\_submodules\protobuf\src\google\protobuf" ( + set "PROTOBUF_PROTO=%repoRoot%\third_party\_submodules\protobuf\src" + ) else ( + for /f "delims=" %%p in ('where protoc 2^>nul') do set "_PROTOC_DIR=%%~dpp" + if defined _PROTOC_DIR ( + set "PROTOBUF_PROTO=!_PROTOC_DIR!..\include" + ) else ( + set "PROTOBUF_PROTO=%repoRoot%\third_party\_submodules\protobuf\src" + ) + ) +) set "TABLEAU_PROTO=%repoRoot%\third_party\_submodules\tableau\proto" set "PLGUIN_DIR=%repoRoot%\cmd\protoc-gen-go-tableau-loader" set "PROTOCONF_IN=%repoRoot%\test\proto" diff --git a/test/go-tableau-loader/gen.sh b/test/go-tableau-loader/gen.sh index bca213dd..0c74e757 100755 --- a/test/go-tableau-loader/gen.sh +++ b/test/go-tableau-loader/gen.sh @@ -7,9 +7,29 @@ set -o pipefail cd "$(git rev-parse --show-toplevel)" # Allow overriding protoc via environment variable. -# Default to the locally compiled protoc for local development. -PROTOC="${PROTOC:-./third_party/_submodules/protobuf/cmake/build/protoc}" -PROTOBUF_PROTO="./third_party/_submodules/protobuf/src" +# Default to locally compiled protoc for local development; fallback to system protoc. +LOCAL_PROTOC="./third_party/_submodules/protobuf/cmake/build/protoc" +if [ -z "$PROTOC" ]; then + if [ -x "$LOCAL_PROTOC" ]; then + PROTOC="$LOCAL_PROTOC" + else + PROTOC="$(which protoc 2>/dev/null || true)" + fi +fi +if [ -z "$PROTOC" ]; then + echo "Error: protoc not found. Please build protobuf submodule or install protoc." >&2 + exit 1 +fi +# Allow overriding protobuf include path via environment variable. +# Default to local submodule source; fallback to system include path. +LOCAL_PROTOBUF_PROTO="./third_party/_submodules/protobuf/src" +if [ -z "$PROTOBUF_PROTO" ]; then + if [ -d "$LOCAL_PROTOBUF_PROTO/google/protobuf" ]; then + PROTOBUF_PROTO="$LOCAL_PROTOBUF_PROTO" + else + PROTOBUF_PROTO="$(pkg-config --variable=includedir protobuf 2>/dev/null || echo /usr/include)" + fi +fi TABLEAU_PROTO="./third_party/_submodules/tableau/proto" PLUGIN_DIR="./cmd/protoc-gen-go-tableau-loader" PROTOCONF_IN="./test/proto" From f19c07a38221e542fb1e17e166fb9898afd94dba Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 12 Mar 2026 20:57:39 +0800 Subject: [PATCH 23/32] Refactor index descriptor tests for improved readability and structure - Simplified the initialization of LevelMessage and IndexDescriptor in tests. - Removed redundant depth and map depth assignments for clarity. - Consolidated index definitions to reduce repetition and enhance maintainability. - Updated test assertions to reflect the new structure and ensure accuracy. - Removed obsolete test for map keys collection as it is no longer relevant. --- .../indexes/generator.go | 6 +- .../indexes/index.go | 107 +- .../indexes/ordered_index.go | 107 +- .../indexes/generator.go | 6 +- .../indexes/index.go | 129 ++- .../indexes/ordered_index.go | 127 ++- internal/index/descriptor.go | 64 +- internal/index/descriptor_test.go | 975 ++++++++---------- 8 files changed, 715 insertions(+), 806 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index 84e9e65b..9c8c1d0f 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -29,11 +29,11 @@ func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, } func (x *Generator) initLevelMessage() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if fd := levelMessage.FD; fd != nil && fd.IsMap() { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + if fd := lm.FD; fd != nil && fd.IsMap() { // Only collect map keys/fds when a deeper level has an index or ordered index, // because these keys are used solely for building upper-level (leveled) containers. - if !levelMessage.NextLevel.NeedGenAnyIndex() { + if !lm.NextLevel.NeedGenAnyIndex() { continue } x.keys = x.keys.AddMapKey(helper.MapKey{ diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go index 6eb25051..eee1b66e 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/index.go @@ -58,8 +58,8 @@ func (x *Generator) genHppIndexFinders() { return } var once sync.Once - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { x.g.P() once.Do(func() { x.g.P(helper.Indent(1), "// Index accessers.") }) x.g.P(helper.Indent(1), "// Index: ", index.Index) @@ -104,27 +104,27 @@ func (x *Generator) genHppIndexFinders() { x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - partKeys := x.keys[:i+1] + for i := 1; i < lm.MapDepth; i++ { + partKeys := x.keys[:i] x.g.P(helper.Indent(1), "// Finds the index: key(", index.Index, ") to value(", vectorType, "),") - x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i+1), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are represented by a vector.") x.g.P(helper.Indent(1), "const ", mapType, "* Find", index.Name(), "Map(", partKeys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level hashmap specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") } x.g.P() x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index, 0), ";") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - if i == 0 { - x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.indexContainerName(index, i+1), ";") + for i := 1; i < lm.MapDepth; i++ { + if i == 1 { + x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.indexContainerName(index, i), ";") } else { - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) - x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.indexContainerName(index, i+1), ";") + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) + x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.indexContainerName(index, i), ";") } } } @@ -137,40 +137,37 @@ func (x *Generator) genIndexLoader() { } defer x.genIndexSorter() x.g.P(helper.Indent(1), "// Index init.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { x.g.P(helper.Indent(1), x.indexContainerName(index, 0), ".clear();") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - x.g.P(helper.Indent(1), x.indexContainerName(index, i+1), ".clear();") + for i := 1; i < lm.MapDepth; i++ { + x.g.P(helper.Indent(1), x.indexContainerName(index, i), ".clear();") } } } parentDataName := "data_" - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.genOneCppIndexLoader(levelMessage, index, parentDataName) - } - itemName := fmt.Sprintf("item%d", levelMessage.Depth+1) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGenIndex() { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + itemName := fmt.Sprintf("item%d", lm.Depth) + if !lm.NeedGenIndex() { break } - x.g.P(helper.Indent(levelMessage.Depth+1), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") + x.g.P(helper.Indent(lm.Depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(lm.FD), ") {") parentDataName = itemName - if levelMessage.FD.IsMap() { - if levelMessage.NeedMapKeyForIndex() { - x.g.P(helper.Indent(levelMessage.Depth+2), "auto k", levelMessage.MapDepth+1, " = ", itemName, ".first;") + if lm.FD.IsMap() { + if lm.NeedMapKeyForIndex() { + x.g.P(helper.Indent(lm.Depth+1), "auto k", lm.MapDepth, " = ", itemName, ".first;") } parentDataName = itemName + ".second" } - defer x.g.P(helper.Indent(levelMessage.Depth+1), "}") + defer x.g.P(helper.Indent(lm.Depth), "}") + for _, index := range lm.Indexes { + x.genOneCppIndexLoader(lm, index, parentDataName) + } } } -func (x *Generator) genOneCppIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { - ident := levelMessage.Depth + 1 +func (x *Generator) genOneCppIndexLoader(lm *index.LevelMessage, index *index.LevelIndex, parentDataName string) { + ident := lm.Depth + 1 x.g.P(helper.Indent(ident), "{") x.g.P(helper.Indent(ident+1), "// Index: ", index.Index) if len(index.ColFields) == 1 { @@ -178,31 +175,31 @@ func (x *Generator) genOneCppIndexLoader(levelMessage *index.LevelMessage, index field := index.ColFields[0] // just take the first field fieldName, _ := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", levelMessage.MapDepth+1) + itemName := fmt.Sprintf("item%d", lm.MapDepth+1) x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") key := itemName if field.FD.Enum() != nil { key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } - x.genLoader(levelMessage, index, ident+2, key, parentDataName) + x.genLoader(lm, index, ident+2, key, parentDataName) x.g.P(helper.Indent(ident+1), "}") } else { key := parentDataName + fieldName - x.genLoader(levelMessage, index, ident+1, key, parentDataName) + x.genLoader(lm, index, ident+1, key, parentDataName) } } else { // multi-column index - x.generateOneCppMulticolumnIndex(levelMessage, index, ident, parentDataName, nil) + x.generateOneCppMulticolumnIndex(lm, index, ident, parentDataName, nil) } x.g.P(helper.Indent(ident), "}") } -func (x *Generator) generateOneCppMulticolumnIndex(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, parentDataName string, keys helper.MapKeySlice) { +func (x *Generator) generateOneCppMulticolumnIndex(lm *index.LevelMessage, index *index.LevelIndex, ident int, parentDataName string, keys helper.MapKeySlice) { cursor := len(keys) if cursor >= len(index.ColFields) { keyType := x.indexMapKeyType(index) x.g.P(helper.Indent(ident+1), keyType, " key{", keys.GenGetArguments(), "};") - x.genLoader(levelMessage, index, ident+1, "key", parentDataName) + x.genLoader(lm, index, ident+1, "key", parentDataName) return } field := index.ColFields[cursor] @@ -215,33 +212,33 @@ func (x *Generator) generateOneCppMulticolumnIndex(levelMessage *index.LevelMess key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnIndex(levelMessage, index, ident+1, parentDataName, keys) + x.generateOneCppMulticolumnIndex(lm, index, ident+1, parentDataName, keys) x.g.P(helper.Indent(ident+1), "}") } else { key := parentDataName + fieldName keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnIndex(levelMessage, index, ident, parentDataName, keys) + x.generateOneCppMulticolumnIndex(lm, index, ident, parentDataName, keys) } } -func (x *Generator) genLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { +func (x *Generator) genLoader(lm *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.indexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - if i == 0 { - x.g.P(helper.Indent(ident), x.indexContainerName(index, i+1), "[k1][", key, "].push_back(&", parentDataName, ");") + for i := 1; i < lm.MapDepth; i++ { + if i == 1 { + x.g.P(helper.Indent(ident), x.indexContainerName(index, i), "[k1][", key, "].push_back(&", parentDataName, ");") } else { var fields []string - for j := 1; j <= i+1; j++ { + for j := 1; j <= i; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - x.g.P(helper.Indent(ident), x.indexContainerName(index, i+1), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") + x.g.P(helper.Indent(ident), x.indexContainerName(index, i), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") } } } func (x *Generator) genIndexSorter() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { if len(index.SortedColFields) != 0 { valueType := x.mapValueType(index) x.g.P(helper.Indent(1), "// Index(sort): ", index.Index) @@ -265,8 +262,8 @@ func (x *Generator) genIndexSorter() { x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(1), "}") // Iterate all leveled containers. - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index, i+1), ") {") + for i := 1; i < lm.MapDepth; i++ { + x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index, i), ") {") x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(2), "}") @@ -281,8 +278,8 @@ func (x *Generator) genCppIndexFinders() { if !x.needGenerateIndex() { return } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { vectorType := x.indexMapValueVectorType(index) mapType := x.indexMapType(index) indexContainerName := x.indexContainerName(index, 0) @@ -317,9 +314,9 @@ func (x *Generator) genCppIndexFinders() { x.g.P("}") x.g.P() - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - indexContainerName := x.indexContainerName(index, i+1) - partKeys := x.keys[:i+1] + for i := 1; i < lm.MapDepth; i++ { + indexContainerName := x.indexContainerName(index, i) + partKeys := x.keys[:i] partParams := partKeys.GenGetParams() partArgs := partKeys.GenGetArguments() x.g.P("const ", messagerName, "::", mapType, "* ", messagerName, "::Find", index.Name(), "Map(", partParams, ") const {") diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go index d13613c8..3142c875 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/ordered_index.go @@ -58,8 +58,8 @@ func (x *Generator) genHppOrderedIndexFinders() { return } var once sync.Once - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { x.g.P() once.Do(func() { x.g.P(helper.Indent(1), "// OrderedIndex accessers.") }) x.g.P(helper.Indent(1), "// OrderedIndex: ", index.Index) @@ -94,27 +94,27 @@ func (x *Generator) genHppOrderedIndexFinders() { x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", keys.GenGetParams(), ") const;") x.g.P(helper.Indent(1), "// Finds the first value of the given key(s).") x.g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", keys.GenGetParams(), ") const;") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - partKeys := x.keys[:i+1] + for i := 1; i < lm.MapDepth; i++ { + partKeys := x.keys[:i] x.g.P(helper.Indent(1), "// Finds the ordered index: key(", index.Index, ") to value(", vectorType, "),") - x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// which is the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are represented by a vector.") x.g.P(helper.Indent(1), "const ", mapType, "* Find", index.Name(), "Map(", partKeys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") - x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partKeys.GenGetArguments(), ").") + x.g.P(helper.Indent(1), "// Finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map specified by (", partKeys.GenGetArguments(), ").") x.g.P(helper.Indent(1), "const ", valueType, "* FindFirst", index.Name(), "(", partKeys.GenGetParams(), ", ", keys.GenGetParams(), ") const;") } x.g.P() x.g.P(" private:") x.g.P(helper.Indent(1), mapType, " ", x.orderedIndexContainerName(index, 0), ";") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - if i == 0 { - x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.orderedIndexContainerName(index, i+1), ";") + for i := 1; i < lm.MapDepth; i++ { + if i == 1 { + x.g.P(helper.Indent(1), "std::unordered_map<", x.keys[0].Type, ", ", mapType, "> ", x.orderedIndexContainerName(index, i), ";") } else { - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) - x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.orderedIndexContainerName(index, i+1), ";") + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) + x.g.P(helper.Indent(1), "std::unordered_map<", levelIndexKeyType, ", ", mapType, ", ", levelIndexKeyType, "Hasher> ", x.orderedIndexContainerName(index, i), ";") } } } @@ -127,40 +127,37 @@ func (x *Generator) genOrderedIndexLoader() { } defer x.genOrderedIndexSorter() x.g.P(helper.Indent(1), "// OrderedIndex init.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, 0), ".clear();") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, i+1), ".clear();") + for i := 1; i < lm.MapDepth; i++ { + x.g.P(helper.Indent(1), x.orderedIndexContainerName(index, i), ".clear();") } } } parentDataName := "data_" - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.genOneCppOrderedIndexLoader(levelMessage, index, parentDataName) - } - itemName := fmt.Sprintf("item%d", levelMessage.Depth+1) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGenOrderedIndex() { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + itemName := fmt.Sprintf("item%d", lm.Depth) + if !lm.NeedGenOrderedIndex() { break } - x.g.P(helper.Indent(levelMessage.Depth+1), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") + x.g.P(helper.Indent(lm.Depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(lm.FD), ") {") parentDataName = itemName - if levelMessage.FD.IsMap() { - if levelMessage.NeedMapKeyForOrderedIndex() { - x.g.P(helper.Indent(levelMessage.Depth+2), "auto k", levelMessage.MapDepth+1, " = ", itemName, ".first;") + if lm.FD.IsMap() { + if lm.NeedMapKeyForOrderedIndex() { + x.g.P(helper.Indent(lm.Depth+1), "auto k", lm.MapDepth, " = ", itemName, ".first;") } parentDataName = itemName + ".second" } - defer x.g.P(helper.Indent(levelMessage.Depth+1), "}") + defer x.g.P(helper.Indent(lm.Depth), "}") + for _, index := range lm.OrderedIndexes { + x.genOneCppOrderedIndexLoader(lm, index, parentDataName) + } } } -func (x *Generator) genOneCppOrderedIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { - ident := levelMessage.Depth + 1 +func (x *Generator) genOneCppOrderedIndexLoader(lm *index.LevelMessage, index *index.LevelIndex, parentDataName string) { + ident := lm.Depth + 1 x.g.P(helper.Indent(ident), "{") x.g.P(helper.Indent(ident+1), "// OrderedIndex: ", index.Index) if len(index.ColFields) == 1 { @@ -168,31 +165,31 @@ func (x *Generator) genOneCppOrderedIndexLoader(levelMessage *index.LevelMessage field := index.ColFields[0] // just take the first field fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", levelMessage.MapDepth+1) + itemName := fmt.Sprintf("item%d", lm.MapDepth+1) x.g.P(helper.Indent(ident+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") key := itemName + suffix if field.FD.Enum() != nil { key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } - x.genOrderedLoader(levelMessage, index, ident+2, key, parentDataName) + x.genOrderedLoader(lm, index, ident+2, key, parentDataName) x.g.P(helper.Indent(ident+1), "}") } else { key := parentDataName + fieldName + suffix - x.genOrderedLoader(levelMessage, index, ident+1, key, parentDataName) + x.genOrderedLoader(lm, index, ident+1, key, parentDataName) } } else { // multi-column index - x.generateOneCppMulticolumnOrderedIndex(levelMessage, index, ident, parentDataName, nil) + x.generateOneCppMulticolumnOrderedIndex(lm, index, ident, parentDataName, nil) } x.g.P(helper.Indent(ident), "}") } -func (x *Generator) generateOneCppMulticolumnOrderedIndex(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, parentDataName string, keys helper.MapKeySlice) { +func (x *Generator) generateOneCppMulticolumnOrderedIndex(lm *index.LevelMessage, index *index.LevelIndex, ident int, parentDataName string, keys helper.MapKeySlice) { cursor := len(keys) if cursor >= len(index.ColFields) { keyType := x.orderedIndexMapKeyType(index) x.g.P(helper.Indent(ident+1), keyType, " key{", keys.GenGetArguments(), "};") - x.genOrderedLoader(levelMessage, index, ident+1, "key", parentDataName) + x.genOrderedLoader(lm, index, ident+1, "key", parentDataName) return } field := index.ColFields[cursor] @@ -205,33 +202,33 @@ func (x *Generator) generateOneCppMulticolumnOrderedIndex(levelMessage *index.Le key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" } keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnOrderedIndex(levelMessage, index, ident+1, parentDataName, keys) + x.generateOneCppMulticolumnOrderedIndex(lm, index, ident+1, parentDataName, keys) x.g.P(helper.Indent(ident+1), "}") } else { key := parentDataName + fieldName + suffix keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneCppMulticolumnOrderedIndex(levelMessage, index, ident, parentDataName, keys) + x.generateOneCppMulticolumnOrderedIndex(lm, index, ident, parentDataName, keys) } } -func (x *Generator) genOrderedLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { +func (x *Generator) genOrderedLoader(lm *index.LevelMessage, index *index.LevelIndex, ident int, key, parentDataName string) { x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, 0), "[", key, "].push_back(&", parentDataName, ");") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - if i == 0 { - x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i+1), "[k1][", key, "].push_back(&", parentDataName, ");") + for i := 1; i < lm.MapDepth; i++ { + if i == 1 { + x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i), "[k1][", key, "].push_back(&", parentDataName, ");") } else { var fields []string - for j := 1; j <= i+1; j++ { + for j := 1; j <= i; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i+1), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") + x.g.P(helper.Indent(ident), x.orderedIndexContainerName(index, i), "[{", strings.Join(fields, ", "), "}][", key, "].push_back(&", parentDataName, ");") } } } func (x *Generator) genOrderedIndexSorter() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { if len(index.SortedColFields) != 0 { valueType := x.mapValueType(index) x.g.P(helper.Indent(1), "// OrderedIndex(sort): ", index.Index) @@ -255,8 +252,8 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(1), "}") // Iterate all leveled containers. - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - x.g.P(helper.Indent(1), "for (auto&& item : ", x.orderedIndexContainerName(index, i+1), ") {") + for i := 1; i < lm.MapDepth; i++ { + x.g.P(helper.Indent(1), "for (auto&& item : ", x.orderedIndexContainerName(index, i), ") {") x.g.P(helper.Indent(2), "for (auto&& item1 : item.second) {") x.g.P(helper.Indent(3), "std::sort(item1.second.begin(), item1.second.end(), ", indexContainerName, "sorter);") x.g.P(helper.Indent(2), "}") @@ -271,8 +268,8 @@ func (x *Generator) genCppOrderedIndexFinders() { if !x.needGenerateOrderedIndex() { return } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { vectorType := x.orderedIndexMapValueVectorType(index) mapType := x.orderedIndexMapType(index) indexContainerName := x.orderedIndexContainerName(index, 0) @@ -307,9 +304,9 @@ func (x *Generator) genCppOrderedIndexFinders() { x.g.P("}") x.g.P() - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - indexContainerNameI := x.orderedIndexContainerName(index, i+1) - partKeys := x.keys[:i+1] + for i := 1; i < lm.MapDepth; i++ { + indexContainerNameI := x.orderedIndexContainerName(index, i) + partKeys := x.keys[:i] partParams := partKeys.GenGetParams() partArgs := partKeys.GenGetArguments() x.g.P("const ", messagerName, "::", mapType, "* ", messagerName, "::Find", index.Name(), "Map(", partParams, ") const {") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index 9fc0d738..6c6b3697 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -31,11 +31,11 @@ func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *i } func (x *Generator) initLevelMessage() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if fd := levelMessage.FD; fd != nil && fd.IsMap() { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + if fd := lm.FD; fd != nil && fd.IsMap() { // Only collect map keys/fds when a deeper level has an index or ordered index, // because these keys are used solely for building upper-level (leveled) containers. - if !levelMessage.NextLevel.NeedGenAnyIndex() { + if !lm.NextLevel.NeedGenAnyIndex() { continue } x.keys = x.keys.AddMapKey(helper.MapKey{ diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/index.go b/cmd/protoc-gen-go-tableau-loader/indexes/index.go index 45e9652a..56420e5a 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/index.go @@ -54,8 +54,8 @@ func (x *Generator) genIndexTypeDef() { return } x.g.P("// Index types.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { x.g.P("// Index: ", index.Index) if len(index.ColFields) != 1 { // multi-column index @@ -80,15 +80,15 @@ func (x *Generator) genIndexField() { if !x.needGenerateIndex() { return } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { x.g.P(x.indexContainerName(index, 0), " ", x.indexMapType(index)) - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - if i == 0 { - x.g.P(x.indexContainerName(index, i+1), " map[", x.keys[0].Type, "]", x.indexMapType(index)) + for i := 1; i < lm.MapDepth; i++ { + if i == 1 { + x.g.P(x.indexContainerName(index, i), " map[", x.keys[0].Type, "]", x.indexMapType(index)) } else { - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) - x.g.P(x.indexContainerName(index, i+1), " map[", levelIndexKeyType, "]", x.indexMapType(index)) + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) + x.g.P(x.indexContainerName(index, i), " map[", levelIndexKeyType, "]", x.indexMapType(index)) } } } @@ -101,47 +101,44 @@ func (x *Generator) genIndexLoader() { } defer x.genIndexSorter() x.g.P("// Index init.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { x.g.P("x.", x.indexContainerName(index, 0), " = make(", x.indexMapType(index), ")") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - if i == 0 { - x.g.P("x.", x.indexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]", x.indexMapType(index), ")") + for i := 1; i < lm.MapDepth; i++ { + if i == 1 { + x.g.P("x.", x.indexContainerName(index, i), " = make(map[", x.keys[0].Type, "]", x.indexMapType(index), ")") } else { - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) - x.g.P("x.", x.indexContainerName(index, i+1), " = make(map[", levelIndexKeyType, "]", x.indexMapType(index), ")") + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) + x.g.P("x.", x.indexContainerName(index, i), " = make(map[", levelIndexKeyType, "]", x.indexMapType(index), ")") } } } } parentDataName := "x.data" - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - x.genOneIndexLoader(levelMessage, index, parentDataName) - } - keyName := fmt.Sprintf("k%d", levelMessage.MapDepth+1) - valueName := fmt.Sprintf("v%d", levelMessage.Depth+1) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGenIndex() { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + keyName := fmt.Sprintf("k%d", lm.MapDepth) + valueName := fmt.Sprintf("v%d", lm.Depth) + if !lm.NeedGenIndex() { break } - if levelMessage.FD.IsMap() { - if levelMessage.NeedMapKeyForIndex() { - x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + if lm.FD.IsMap() { + if lm.NeedMapKeyForIndex() { + x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(lm.FD), " {") } else { - x.g.P("for _, ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + x.g.P("for _, ", valueName, " := range ", parentDataName, x.fieldGetter(lm.FD), " {") } } else { - x.g.P("for _ , ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + x.g.P("for _ , ", valueName, " := range ", parentDataName, x.fieldGetter(lm.FD), " {") } parentDataName = valueName defer x.g.P("}") + for _, index := range lm.Indexes { + x.genOneIndexLoader(lm, index, parentDataName) + } } } -func (x *Generator) genOneIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { +func (x *Generator) genOneIndexLoader(lm *index.LevelMessage, index *index.LevelIndex, parentDataName string) { x.g.P("{") x.g.P("// Index: ", index.Index) if len(index.ColFields) == 1 { @@ -149,28 +146,28 @@ func (x *Generator) genOneIndexLoader(levelMessage *index.LevelMessage, index *i field := index.ColFields[0] // just take the first field fieldName, _ := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - valueName := fmt.Sprintf("v%d", levelMessage.MapDepth+1) + valueName := fmt.Sprintf("v%d", lm.MapDepth+1) x.g.P("for _ , ", valueName, " := range ", parentDataName, fieldName, " {") x.g.P("key := ", valueName) - x.genIndexLoaderCommon(levelMessage, index, parentDataName) + x.genIndexLoaderCommon(lm, index, parentDataName) x.g.P("}") } else { x.g.P("key := ", parentDataName, fieldName) - x.genIndexLoaderCommon(levelMessage, index, parentDataName) + x.genIndexLoaderCommon(lm, index, parentDataName) } } else { // multi-column index - x.generateOneMulticolumnIndex(levelMessage, index, parentDataName, nil) + x.generateOneMulticolumnIndex(lm, index, parentDataName, nil) } x.g.P("}") } -func (x *Generator) generateOneMulticolumnIndex(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { +func (x *Generator) generateOneMulticolumnIndex(lm *index.LevelMessage, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { cursor := len(keys) if cursor >= len(index.ColFields) { keyType := x.indexMapKeyType(index) x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") - x.genIndexLoaderCommon(levelMessage, index, parentDataName) + x.genIndexLoaderCommon(lm, index, parentDataName) return } field := index.ColFields[cursor] @@ -179,31 +176,31 @@ func (x *Generator) generateOneMulticolumnIndex(levelMessage *index.LevelMessage itemName := fmt.Sprintf("indexItem%d", cursor) x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") keys = keys.AddMapKey(helper.MapKey{Name: itemName}) - x.generateOneMulticolumnIndex(levelMessage, index, parentDataName, keys) + x.generateOneMulticolumnIndex(lm, index, parentDataName, keys) x.g.P("}") } else { key := parentDataName + fieldName keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnIndex(levelMessage, index, parentDataName, keys) + x.generateOneMulticolumnIndex(lm, index, parentDataName, keys) } } -func (x *Generator) genIndexLoaderCommon(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { +func (x *Generator) genIndexLoaderCommon(lm *index.LevelMessage, index *index.LevelIndex, parentDataName string) { indexContainerName := x.indexContainerName(index, 0) x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - indexContainerName := x.indexContainerName(index, i+1) - if i == 0 { + for i := 1; i < lm.MapDepth; i++ { + indexContainerName := x.indexContainerName(index, i) + if i == 1 { x.g.P("if x.", indexContainerName, "[k1] == nil {") x.g.P("x.", indexContainerName, "[k1] = make(", x.indexMapType(index), ")") x.g.P("}") x.g.P("x.", indexContainerName, "[k1][key] = append(x.", indexContainerName, "[k1][key], ", parentDataName, ")") } else { var fields []string - for j := 1; j <= i+1; j++ { + for j := 1; j <= i; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) keyName := indexContainerName + "Keys" x.g.P(keyName, " := ", levelIndexKeyType, "{", strings.Join(fields, ", "), "}") x.g.P("if x.", indexContainerName, "[", keyName, "] == nil {") @@ -215,8 +212,8 @@ func (x *Generator) genIndexLoaderCommon(levelMessage *index.LevelMessage, index } func (x *Generator) genIndexSorter() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { if len(index.SortedColFields) != 0 { x.g.P("// Index(sort): ", index.Index) indexContainerName := x.indexContainerName(index, 0) @@ -238,8 +235,8 @@ func (x *Generator) genIndexSorter() { x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("}") // Iterate all leveled containers. - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - x.g.P("for _, itemMap := range x.", x.indexContainerName(index, i+1), " {") + for i := 1; i < lm.MapDepth; i++ { + x.g.P("for _, itemMap := range x.", x.indexContainerName(index, i), " {") x.g.P("for _, itemList := range itemMap {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("}") @@ -254,8 +251,8 @@ func (x *Generator) genIndexFinders() { if !x.needGenerateIndex() { return } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.Indexes { indexContainerName := x.indexContainerName(index, 0) messagerName := x.messagerName() x.g.P("// Index: ", index.Index) @@ -292,40 +289,40 @@ func (x *Generator) genIndexFinders() { x.g.P("}") x.g.P() - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - indexContainerName := x.indexContainerName(index, i+1) - partKeys := x.keys[:i+1] + for i := 1; i < lm.MapDepth; i++ { + indexContainerName := x.indexContainerName(index, i) + partKeys := x.keys[:i] partParams := partKeys.GenGetParams() partArgs := partKeys.GenGetArguments() - x.g.P("// Find", index.Name(), "Map", i+1, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") - x.g.P("// which is the upper ", loadutil.Ordinal(i+1), "-level map specified by (", partArgs, ").") + x.g.P("// Find", index.Name(), "Map", i, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") + x.g.P("// which is the upper ", loadutil.Ordinal(i), "-level map specified by (", partArgs, ").") x.g.P("// One key may correspond to multiple values, which are represented by a slice.") - x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i+1, "(", partParams, ") ", x.indexMapType(index), " {") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i, "(", partParams, ") ", x.indexMapType(index), " {") if len(partKeys) == 1 { x.g.P("return x.", indexContainerName, "[", partArgs, "]") } else { - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) x.g.P("return x.", indexContainerName, "[", levelIndexKeyType, "{", partArgs, "}]") } x.g.P("}") x.g.P() - x.g.P("// Find", index.Name(), i+1, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level map") + x.g.P("// Find", index.Name(), i, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map") x.g.P("// specified by (", partArgs, ").") - x.g.P("func (x *", messagerName, ") Find", index.Name(), i+1, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") + x.g.P("func (x *", messagerName, ") Find", index.Name(), i, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") if len(index.ColFields) == 1 { - x.g.P("return x.Find", index.Name(), "Map", i+1, "(", partArgs, ")[", args, "]") + x.g.P("return x.Find", index.Name(), "Map", i, "(", partArgs, ")[", args, "]") } else { - x.g.P("return x.Find", index.Name(), "Map", i+1, "(", partArgs, ")[", x.indexMapKeyType(index), "{", args, "}]") + x.g.P("return x.Find", index.Name(), "Map", i, "(", partArgs, ")[", x.indexMapKeyType(index), "{", args, "}]") } x.g.P("}") x.g.P() - x.g.P("// FindFirst", index.Name(), i+1, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level map") + x.g.P("// FindFirst", index.Name(), i, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level map") x.g.P("// specified by (", partArgs, "), or nil if no value found.") - x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i+1, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") - x.g.P("val := x.Find", index.Name(), i+1, "(", partArgs, ", ", args, ")") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), i, "(", partArgs, ", ", args, ")") x.g.P("if len(val) > 0 {") x.g.P("return val[0]") x.g.P("}") diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go index aaeff57b..49e765e8 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/ordered_index.go @@ -64,8 +64,8 @@ func (x *Generator) genOrderedIndexTypeDef() { return } x.g.P("// OrderedIndex types.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { x.g.P("// OrderedIndex: ", index.Index) if len(index.ColFields) != 1 { // multi-column index @@ -104,15 +104,15 @@ func (x *Generator) genOrderedIndexField() { if !x.needGenerateOrderedIndex() { return } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { x.g.P(x.orderedIndexContainerName(index, 0), " *", x.orderedIndexMapType(index)) - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - if i == 0 { - x.g.P(x.orderedIndexContainerName(index, i+1), " map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index)) + for i := 1; i < lm.MapDepth; i++ { + if i == 1 { + x.g.P(x.orderedIndexContainerName(index, i), " map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index)) } else { - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) - x.g.P(x.orderedIndexContainerName(index, i+1), " map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index)) + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) + x.g.P(x.orderedIndexContainerName(index, i), " map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index)) } } } @@ -125,47 +125,44 @@ func (x *Generator) genOrderedIndexLoader() { } defer x.genOrderedIndexSorter() x.g.P("// OrderedIndex init.") - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { x.g.P("x.", x.orderedIndexContainerName(index, 0), " = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - if i == 0 { - x.g.P("x.", x.orderedIndexContainerName(index, i+1), " = make(map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index), ")") + for i := 1; i < lm.MapDepth; i++ { + if i == 1 { + x.g.P("x.", x.orderedIndexContainerName(index, i), " = make(map[", x.keys[0].Type, "]*", x.orderedIndexMapType(index), ")") } else { - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) - x.g.P("x.", x.orderedIndexContainerName(index, i+1), " = make(map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index), ")") + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) + x.g.P("x.", x.orderedIndexContainerName(index, i), " = make(map[", levelIndexKeyType, "]*", x.orderedIndexMapType(index), ")") } } } } parentDataName := "x.data" - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - x.genOneOrderedIndexLoader(levelMessage, index, parentDataName) - } - keyName := fmt.Sprintf("k%d", levelMessage.MapDepth+1) - valueName := fmt.Sprintf("v%d", levelMessage.Depth+1) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGenOrderedIndex() { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + keyName := fmt.Sprintf("k%d", lm.MapDepth) + valueName := fmt.Sprintf("v%d", lm.Depth) + if !lm.NeedGenOrderedIndex() { break } - if levelMessage.FD.IsMap() { - if levelMessage.NeedMapKeyForOrderedIndex() { - x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + if lm.FD.IsMap() { + if lm.NeedMapKeyForOrderedIndex() { + x.g.P("for ", keyName, ", ", valueName, " := range ", parentDataName, x.fieldGetter(lm.FD), " {") } else { - x.g.P("for _, ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + x.g.P("for _, ", valueName, " := range ", parentDataName, x.fieldGetter(lm.FD), " {") } } else { - x.g.P("for _ , ", valueName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + x.g.P("for _ , ", valueName, " := range ", parentDataName, x.fieldGetter(lm.FD), " {") } parentDataName = valueName defer x.g.P("}") + for _, index := range lm.OrderedIndexes { + x.genOneOrderedIndexLoader(lm, index, parentDataName) + } } } -func (x *Generator) genOneOrderedIndexLoader(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { +func (x *Generator) genOneOrderedIndexLoader(lm *index.LevelMessage, index *index.LevelIndex, parentDataName string) { x.g.P("{") x.g.P("// OrderedIndex: ", index.Index) if len(index.ColFields) == 1 { @@ -173,28 +170,28 @@ func (x *Generator) genOneOrderedIndexLoader(levelMessage *index.LevelMessage, i field := index.ColFields[0] // just take the first field fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) if field.FD.IsList() { - valueName := fmt.Sprintf("v%d", levelMessage.MapDepth+1) + valueName := fmt.Sprintf("v%d", lm.MapDepth+1) x.g.P("for _ , ", valueName, " := range ", parentDataName, fieldName, " {") x.g.P("key := ", valueName, suffix) - x.genOrderedIndexLoaderCommon(levelMessage, index, parentDataName) + x.genOrderedIndexLoaderCommon(lm, index, parentDataName) x.g.P("}") } else { x.g.P("key := ", parentDataName, fieldName, suffix) - x.genOrderedIndexLoaderCommon(levelMessage, index, parentDataName) + x.genOrderedIndexLoaderCommon(lm, index, parentDataName) } } else { // multi-column index - x.generateOneMulticolumnOrderedIndex(levelMessage, index, parentDataName, nil) + x.generateOneMulticolumnOrderedIndex(lm, index, parentDataName, nil) } x.g.P("}") } -func (x *Generator) generateOneMulticolumnOrderedIndex(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { +func (x *Generator) generateOneMulticolumnOrderedIndex(lm *index.LevelMessage, index *index.LevelIndex, parentDataName string, keys helper.MapKeySlice) { cursor := len(keys) if cursor >= len(index.ColFields) { keyType := x.orderedIndexMapKeyType(index) x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") - x.genOrderedIndexLoaderCommon(levelMessage, index, parentDataName) + x.genOrderedIndexLoaderCommon(lm, index, parentDataName) return } field := index.ColFields[cursor] @@ -204,23 +201,23 @@ func (x *Generator) generateOneMulticolumnOrderedIndex(levelMessage *index.Level x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") key := itemName + suffix keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnOrderedIndex(levelMessage, index, parentDataName, keys) + x.generateOneMulticolumnOrderedIndex(lm, index, parentDataName, keys) x.g.P("}") } else { key := parentDataName + fieldName + suffix keys = keys.AddMapKey(helper.MapKey{Name: key}) - x.generateOneMulticolumnOrderedIndex(levelMessage, index, parentDataName, keys) + x.generateOneMulticolumnOrderedIndex(lm, index, parentDataName, keys) } } -func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage, index *index.LevelIndex, parentDataName string) { +func (x *Generator) genOrderedIndexLoaderCommon(lm *index.LevelMessage, index *index.LevelIndex, parentDataName string) { indexContainerName := x.orderedIndexContainerName(index, 0) x.g.P("value, _ := x.", indexContainerName, ".Get(key)") x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - orderedIndexContainerName := x.orderedIndexContainerName(index, i+1) + for i := 1; i < lm.MapDepth; i++ { + orderedIndexContainerName := x.orderedIndexContainerName(index, i) valueName := orderedIndexContainerName + "Value" - if i == 0 { + if i == 1 { x.g.P("if x.", orderedIndexContainerName, "[k1] == nil {") x.g.P("x.", orderedIndexContainerName, "[k1] = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.orderedIndexMapKeyType(index), ", []*", x.mapValueType(index), "]()") x.g.P("}") @@ -228,10 +225,10 @@ func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage x.g.P("x.", orderedIndexContainerName, "[k1].Put(key, append(", valueName, ", ", parentDataName, "))") } else { var fields []string - for j := 1; j <= i+1; j++ { + for j := 1; j <= i; j++ { fields = append(fields, fmt.Sprintf("k%d", j)) } - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) keyName := orderedIndexContainerName + "Keys" x.g.P(keyName, " := ", levelIndexKeyType, "{", strings.Join(fields, ", "), "}") x.g.P("if x.", orderedIndexContainerName, "[", keyName, "] == nil {") @@ -244,8 +241,8 @@ func (x *Generator) genOrderedIndexLoaderCommon(levelMessage *index.LevelMessage } func (x *Generator) genOrderedIndexSorter() { - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { if len(index.SortedColFields) != 0 { x.g.P("// OrderedIndex(sort): ", index.Index) indexContainerName := x.orderedIndexContainerName(index, 0) @@ -268,8 +265,8 @@ func (x *Generator) genOrderedIndexSorter() { x.g.P("return true") x.g.P("})") // Iterate all leveled containers. - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - x.g.P("for _, itemMap := range x.", x.orderedIndexContainerName(index, i+1), " {") + for i := 1; i < lm.MapDepth; i++ { + x.g.P("for _, itemMap := range x.", x.orderedIndexContainerName(index, i), " {") x.g.P("itemMap.Range(func(key ", x.orderedIndexMapKeyType(index), ", itemList []*", x.mapValueType(index), ") bool {") x.g.P(helper.SortPackage.Ident("Slice"), "(itemList, ", indexContainerName, "Sorter(itemList))") x.g.P("return true") @@ -285,8 +282,8 @@ func (x *Generator) genOrderedIndexFinders() { if !x.needGenerateOrderedIndex() { return } - for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { + for lm := x.descriptor.LevelMessage; lm != nil; lm = lm.NextLevel { + for _, index := range lm.OrderedIndexes { indexContainerName := x.orderedIndexContainerName(index, 0) messagerName := x.messagerName() x.g.P("// OrderedIndex: ", index.Index) @@ -324,29 +321,29 @@ func (x *Generator) genOrderedIndexFinders() { x.g.P("}") x.g.P() - for i := 0; i < levelMessage.UpperMapLevels(); i++ { - orderedIndexContainerName := x.orderedIndexContainerName(index, i+1) - partKeys := x.keys[:i+1] + for i := 1; i < lm.MapDepth; i++ { + orderedIndexContainerName := x.orderedIndexContainerName(index, i) + partKeys := x.keys[:i] partParams := partKeys.GenGetParams() partArgs := partKeys.GenGetArguments() - x.g.P("// Find", index.Name(), "Map", i+1, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") - x.g.P("// which is the upper ", loadutil.Ordinal(i+1), "-level treemap specified by (", partArgs, ").") + x.g.P("// Find", index.Name(), "Map", i, " finds the index: key(", index.Index, ") to value(", x.mapValueType(index), "),") + x.g.P("// which is the upper ", loadutil.Ordinal(i), "-level treemap specified by (", partArgs, ").") x.g.P("// One key may correspond to multiple values, which are represented by a slice.") - x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i+1, "(", partParams, ") *", x.orderedIndexMapType(index), " {") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map", i, "(", partParams, ") *", x.orderedIndexMapType(index), " {") if len(partKeys) == 1 { x.g.P("return x.", orderedIndexContainerName, "[", partArgs, "]") } else { - levelIndexKeyType := x.levelKeyType(x.keys[i].Fd) + levelIndexKeyType := x.levelKeyType(x.keys[i-1].Fd) x.g.P("return x.", orderedIndexContainerName, "[", levelIndexKeyType, "{", partArgs, "}]") } x.g.P("}") x.g.P() - x.g.P("// Find", index.Name(), i+1, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level treemap") + x.g.P("// Find", index.Name(), i, " finds a slice of all values of the given key(s) in the upper ", loadutil.Ordinal(i), "-level treemap") x.g.P("// specified by (", partArgs, ").") - x.g.P("func (x *", messagerName, ") Find", index.Name(), i+1, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") - x.g.P("m := x.Find", index.Name(), "Map", i+1, "(", partArgs, ")") + x.g.P("func (x *", messagerName, ") Find", index.Name(), i, "(", partParams, ", ", params, ") []*", x.mapValueType(index), " {") + x.g.P("m := x.Find", index.Name(), "Map", i, "(", partArgs, ")") x.g.P("if m == nil {") x.g.P("return nil") x.g.P("}") @@ -359,10 +356,10 @@ func (x *Generator) genOrderedIndexFinders() { x.g.P("}") x.g.P() - x.g.P("// FindFirst", index.Name(), i+1, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i+1), "-level treemap") + x.g.P("// FindFirst", index.Name(), i, " finds the first value of the given key(s) in the upper ", loadutil.Ordinal(i), "-level treemap") x.g.P("// specified by (", partArgs, "), or nil if no value found.") - x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i+1, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") - x.g.P("val := x.Find", index.Name(), i+1, "(", partArgs, ", ", args, ")") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), i, "(", partParams, ", ", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), i, "(", partArgs, ", ", args, ")") x.g.P("if len(val) > 0 {") x.g.P("return val[0]") x.g.P("}") diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index 82f382cd..93202064 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -46,8 +46,8 @@ type LevelField struct { type LevelMessage struct { NextLevel *LevelMessage - // Current level message's field which contains index fields. - // Only valid when NextLevel is not nil. + // FD is the container field (map or list) through which this level is entered + // from its parent. It is nil for the top-level (root) LevelMessage. FD protoreflect.FieldDescriptor // Current level message's all index fields @@ -57,18 +57,11 @@ type LevelMessage struct { // For example, the top-level message has Depth=0, the next level has Depth=1, and so on. Depth int // MapDepth is the 0-based map depth of message hierarchy. - // It only increments when the next level is a map field. - // For example, the top-level message has MapDepth=0, the next level (if map) has MapDepth=1, and so on. + // It only increments when the current level is entered via a map field (i.e., FD.IsMap()). + // For example, the top-level message has MapDepth=0, the next level (if entered via map) has MapDepth=1, and so on. MapDepth int } -// UpperMapLevels returns the number of upper map levels above this level. -// For example, an index at map depth 2 has 1 upper map layer (keyed by k1), -// and an index at map depth 3 has 2 upper map levels (keyed by k1 and {k1, k2}). -func (l *LevelMessage) UpperMapLevels() int { - return l.MapDepth - 1 -} - func (l *LevelMessage) NeedGenIndex() bool { if l == nil { return false @@ -96,7 +89,7 @@ func (l *LevelMessage) NeedGenAnyIndex() bool { // level and all deeper levels for indexes. func (l *LevelMessage) NeedMapKeyForIndex() bool { for lm := l.NextLevel; lm != nil; lm = lm.NextLevel { - if lm.MapDepth > l.MapDepth+1 { + if lm.MapDepth > l.MapDepth { return lm.NeedGenIndex() } } @@ -107,7 +100,7 @@ func (l *LevelMessage) NeedMapKeyForIndex() bool { // needed by any deeper level's ordered index's leveled containers. func (l *LevelMessage) NeedMapKeyForOrderedIndex() bool { for lm := l.NextLevel; lm != nil; lm = lm.NextLevel { - if lm.MapDepth > l.MapDepth+1 { + if lm.MapDepth > l.MapDepth { return lm.NeedGenOrderedIndex() } } @@ -130,20 +123,19 @@ func (l *LevelIndex) Name() string { return name } -func parseLevelMessage(md protoreflect.MessageDescriptor, depth, mapDepth int) *LevelMessage { +func parseLevelMessage(fd protoreflect.FieldDescriptor, md protoreflect.MessageDescriptor, depth, mapDepth int) *LevelMessage { levelMsg := &LevelMessage{ + FD: fd, Depth: depth, MapDepth: mapDepth, } for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() && fd.MapValue().Kind() == protoreflect.MessageKind { - levelMsg.NextLevel = parseLevelMessage(fd.MapValue().Message(), depth+1, mapDepth+1) - levelMsg.FD = fd + childFD := md.Fields().Get(i) + if childFD.IsMap() && childFD.MapValue().Kind() == protoreflect.MessageKind { + levelMsg.NextLevel = parseLevelMessage(childFD, childFD.MapValue().Message(), depth+1, mapDepth+1) return levelMsg - } else if fd.IsList() && fd.Kind() == protoreflect.MessageKind { - levelMsg.NextLevel = parseLevelMessage(fd.Message(), depth+1, mapDepth) - levelMsg.FD = fd + } else if childFD.IsList() && childFD.Kind() == protoreflect.MessageKind { + levelMsg.NextLevel = parseLevelMessage(childFD, childFD.Message(), depth+1, mapDepth) return levelMsg } } @@ -151,7 +143,7 @@ func parseLevelMessage(md protoreflect.MessageDescriptor, depth, mapDepth int) * } // parseRecursively parses multi-column index related info. -func parseRecursively(index *Index, prefix string, md protoreflect.MessageDescriptor, levelMessage *LevelMessage, ordered bool) { +func parseRecursively(index *Index, prefix string, md protoreflect.MessageDescriptor, lm *LevelMessage, ordered bool) { colFields := parseInSameLevel(index.Cols, prefix, md, nil) sortedColFields := parseInSameLevel(index.SortedCols, prefix, md, nil) if len(colFields) != 0 { @@ -163,20 +155,20 @@ func parseRecursively(index *Index, prefix string, md protoreflect.MessageDescri SortedColFields: sortedColFields, } if ordered { - levelMessage.OrderedIndexes = append(levelMessage.OrderedIndexes, levelIndex) + lm.OrderedIndexes = append(lm.OrderedIndexes, levelIndex) } else { - levelMessage.Indexes = append(levelMessage.Indexes, levelIndex) + lm.Indexes = append(lm.Indexes, levelIndex) } - } else if levelMessage != nil && levelMessage.NextLevel != nil { + } else if lm != nil && lm.NextLevel != nil { // index invalid or belongs to deeper level - fd := levelMessage.FD + fd := lm.NextLevel.FD opts := fd.Options().(*descriptorpb.FieldOptions) fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) fieldOptName := fdOpts.GetName() if fd.IsMap() { - parseRecursively(index, prefix+fieldOptName, fd.MapValue().Message(), levelMessage.NextLevel, ordered) + parseRecursively(index, prefix+fieldOptName, fd.MapValue().Message(), lm.NextLevel, ordered) } else { - parseRecursively(index, prefix+fieldOptName, fd.Message(), levelMessage.NextLevel, ordered) + parseRecursively(index, prefix+fieldOptName, fd.Message(), lm.NextLevel, ordered) } } } @@ -224,27 +216,25 @@ func parseCols(cols []string, prefix string, md protoreflect.MessageDescriptor, } func ParseIndexDescriptor(md protoreflect.MessageDescriptor) *IndexDescriptor { - descriptor := &IndexDescriptor{ - LevelMessage: parseLevelMessage(md, 0, 0), - } + levelMessage := parseLevelMessage(nil, md, 0, 0) indexes, orderedIndexes := ParseWSOptionIndex(md) // parse indexes into level message for _, index := range indexes { if len(index.Cols) == 0 { continue } - parseRecursively(index, "", md, descriptor.LevelMessage, false) + parseRecursively(index, "", md, levelMessage, false) } for _, index := range orderedIndexes { if len(index.Cols) == 0 { continue } - parseRecursively(index, "", md, descriptor.LevelMessage, true) + parseRecursively(index, "", md, levelMessage, true) } // check duplicate index name indexNameMap := map[string]*Index{} - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - allIndexes := append(levelMessage.Indexes, levelMessage.OrderedIndexes...) + for lm := levelMessage; lm != nil; lm = lm.NextLevel { + allIndexes := append(lm.Indexes, lm.OrderedIndexes...) for _, index := range allIndexes { name := index.Name() if existingIndex, ok := indexNameMap[name]; ok { @@ -254,5 +244,7 @@ func ParseIndexDescriptor(md protoreflect.MessageDescriptor) *IndexDescriptor { } } } - return descriptor + return &IndexDescriptor{ + LevelMessage: levelMessage.NextLevel, + } } diff --git a/internal/index/descriptor_test.go b/internal/index/descriptor_test.go index 94cae9b7..df7d4f54 100644 --- a/internal/index/descriptor_test.go +++ b/internal/index/descriptor_test.go @@ -34,261 +34,257 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 0, - MapDepth: 0, FD: fd[*protoconf.ItemConf]("item_map"), - NextLevel: &LevelMessage{ - Depth: 1, - MapDepth: 1, - Indexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"Type"}, - Name: "", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("type"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("type"), - }, + Depth: 1, + MapDepth: 1, + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"Type"}, + Name: "", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("type"), }, }, }, - { - Index: &Index{ - Cols: []string{"Param"}, - SortedCols: []string{"ID"}, - Name: "ItemInfo", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("param_list"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("param_list"), - }, + }, + { + Index: &Index{ + Cols: []string{"Param"}, + SortedCols: []string{"ID"}, + Name: "ItemInfo", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("param_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("param_list"), }, }, - SortedColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("id"), - }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("id"), }, }, }, - { - Index: &Index{ - Cols: []string{"Default"}, - Name: "ItemDefaultInfo", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("default"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("default"), - }, + }, + { + Index: &Index{ + Cols: []string{"Default"}, + Name: "ItemDefaultInfo", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("default"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("default"), }, }, }, - { - Index: &Index{ - Cols: []string{"ExtType"}, - Name: "ItemExtInfo", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("ext_type_list"), - }, + }, + { + Index: &Index{ + Cols: []string{"ExtType"}, + Name: "ItemExtInfo", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("ext_type_list"), }, }, }, - { - Index: &Index{ - Cols: []string{"ID", "Name"}, - SortedCols: []string{"Type", "UseEffectType"}, - Name: "AwardItem", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("id"), - }, + }, + { + Index: &Index{ + Cols: []string{"ID", "Name"}, + SortedCols: []string{"Type", "UseEffectType"}, + Name: "AwardItem", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("id"), }, - { - FD: fd[*protoconf.ItemConf_Item]("name"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("name"), - }, + }, + { + FD: fd[*protoconf.ItemConf_Item]("name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("name"), }, }, - SortedColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("type"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("type"), - }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("type"), }, - { - FD: fd[*protoconf.UseEffect]("type"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("use_effect"), - fd[*protoconf.UseEffect]("type"), - }, + }, + { + FD: fd[*protoconf.UseEffect]("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("use_effect"), + fd[*protoconf.UseEffect]("type"), }, }, }, - { - Index: &Index{ - Cols: []string{"ID", "Type", "Param", "ExtType"}, - Name: "SpecialItem", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("id"), - }, + }, + { + Index: &Index{ + Cols: []string{"ID", "Type", "Param", "ExtType"}, + Name: "SpecialItem", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("id"), }, - { - FD: fd[*protoconf.ItemConf_Item]("type"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("type"), - }, + }, + { + FD: fd[*protoconf.ItemConf_Item]("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("type"), }, - { - FD: fd[*protoconf.ItemConf_Item]("param_list"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("param_list"), - }, + }, + { + FD: fd[*protoconf.ItemConf_Item]("param_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("param_list"), }, - { - FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("ext_type_list"), - }, + }, + { + FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("ext_type_list"), }, }, }, - { - Index: &Index{ - Cols: []string{"PathDir"}, - Name: "ItemPathDir", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.Path]("dir"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("path"), - fd[*protoconf.Path]("dir"), - }, + }, + { + Index: &Index{ + Cols: []string{"PathDir"}, + Name: "ItemPathDir", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Path]("dir"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("path"), + fd[*protoconf.Path]("dir"), }, }, }, - { - Index: &Index{ - Cols: []string{"PathName"}, - Name: "ItemPathName", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.Path]("name_list"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("path"), - fd[*protoconf.Path]("name_list"), - }, + }, + { + Index: &Index{ + Cols: []string{"PathName"}, + Name: "ItemPathName", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Path]("name_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("path"), + fd[*protoconf.Path]("name_list"), }, }, }, - { - Index: &Index{ - Cols: []string{"PathFriendID"}, - Name: "ItemPathFriendID", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.Path_Friend]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("path"), - fd[*protoconf.Path]("friend"), - fd[*protoconf.Path_Friend]("id"), - }, + }, + { + Index: &Index{ + Cols: []string{"PathFriendID"}, + Name: "ItemPathFriendID", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Path_Friend]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("path"), + fd[*protoconf.Path]("friend"), + fd[*protoconf.Path_Friend]("id"), }, }, }, - { - Index: &Index{ - Cols: []string{"UseEffectType"}, - Name: "UseEffectType", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.UseEffect]("type"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("use_effect"), - fd[*protoconf.UseEffect]("type"), - }, + }, + { + Index: &Index{ + Cols: []string{"UseEffectType"}, + Name: "UseEffectType", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.UseEffect]("type"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("use_effect"), + fd[*protoconf.UseEffect]("type"), }, }, }, }, - OrderedIndexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"ExtType"}, - Name: "ExtType", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("ext_type_list"), - }, + }, + OrderedIndexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"ExtType"}, + Name: "ExtType", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("ext_type_list"), }, }, }, - { - Index: &Index{ - Cols: []string{"Param", "ExtType"}, - SortedCols: []string{"ID"}, - Name: "ParamExtType", - }, - MD: md[*protoconf.ItemConf_Item](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("param_list"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("param_list"), - }, + }, + { + Index: &Index{ + Cols: []string{"Param", "ExtType"}, + SortedCols: []string{"ID"}, + Name: "ParamExtType", + }, + MD: md[*protoconf.ItemConf_Item](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("param_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("param_list"), }, - { - FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("ext_type_list"), - }, + }, + { + FD: fd[*protoconf.ItemConf_Item]("ext_type_list"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("ext_type_list"), }, }, - SortedColFields: []*LevelField{ - { - FD: fd[*protoconf.ItemConf_Item]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ItemConf_Item]("id"), - }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.ItemConf_Item]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ItemConf_Item]("id"), }, }, }, @@ -304,29 +300,25 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 0, - MapDepth: 0, FD: fd[*protoconf.HeroConf]("hero_map"), + Depth: 1, + MapDepth: 1, NextLevel: &LevelMessage{ - Depth: 1, - MapDepth: 1, FD: fd[*protoconf.HeroConf_Hero]("attr_map"), - NextLevel: &LevelMessage{ - Depth: 2, - MapDepth: 2, - Indexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"Title"}, - Name: "", - }, - MD: md[*protoconf.HeroConf_Hero_Attr](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.HeroConf_Hero_Attr]("title"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.HeroConf_Hero_Attr]("title"), - }, + Depth: 2, + MapDepth: 2, + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"Title"}, + Name: "", + }, + MD: md[*protoconf.HeroConf_Hero_Attr](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.HeroConf_Hero_Attr]("title"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.HeroConf_Hero_Attr]("title"), }, }, }, @@ -343,104 +335,100 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 0, - MapDepth: 0, FD: fd[*protoconf.ActivityConf]("activity_map"), + Depth: 1, + MapDepth: 1, + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"ActivityName"}, + Name: "", + }, + MD: md[*protoconf.ActivityConf_Activity](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ActivityConf_Activity]("activity_name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ActivityConf_Activity]("activity_name"), + }, + }, + }, + }, + }, NextLevel: &LevelMessage{ - Depth: 1, - MapDepth: 1, FD: fd[*protoconf.ActivityConf_Activity]("chapter_map"), + Depth: 2, + MapDepth: 2, Indexes: []*LevelIndex{ { Index: &Index{ - Cols: []string{"ActivityName"}, + Cols: []string{"ChapterID"}, Name: "", }, - MD: md[*protoconf.ActivityConf_Activity](), + MD: md[*protoconf.ActivityConf_Activity_Chapter](), ColFields: []*LevelField{ { - FD: fd[*protoconf.ActivityConf_Activity]("activity_name"), + FD: fd[*protoconf.ActivityConf_Activity_Chapter]("chapter_id"), LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ActivityConf_Activity]("activity_name"), + fd[*protoconf.ActivityConf_Activity_Chapter]("chapter_id"), }, }, }, }, - }, - NextLevel: &LevelMessage{ - Depth: 2, - MapDepth: 2, - FD: fd[*protoconf.ActivityConf_Activity_Chapter]("section_map"), - Indexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"ChapterID"}, - Name: "", - }, - MD: md[*protoconf.ActivityConf_Activity_Chapter](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ActivityConf_Activity_Chapter]("chapter_id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ActivityConf_Activity_Chapter]("chapter_id"), - }, - }, - }, + { + Index: &Index{ + Cols: []string{"ChapterName"}, + SortedCols: []string{"AwardID"}, + Name: "NamedChapter", }, - { - Index: &Index{ - Cols: []string{"ChapterName"}, - SortedCols: []string{"AwardID"}, - Name: "NamedChapter", - }, - MD: md[*protoconf.ActivityConf_Activity_Chapter](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.ActivityConf_Activity_Chapter]("chapter_name"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ActivityConf_Activity_Chapter]("chapter_name"), - }, + MD: md[*protoconf.ActivityConf_Activity_Chapter](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.ActivityConf_Activity_Chapter]("chapter_name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ActivityConf_Activity_Chapter]("chapter_name"), }, }, - SortedColFields: []*LevelField{ - { - FD: fd[*protoconf.ActivityConf_Activity_Chapter]("award_id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.ActivityConf_Activity_Chapter]("award_id"), - }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.ActivityConf_Activity_Chapter]("award_id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.ActivityConf_Activity_Chapter]("award_id"), }, }, }, }, + }, + NextLevel: &LevelMessage{ + FD: fd[*protoconf.ActivityConf_Activity_Chapter]("section_map"), + Depth: 3, + MapDepth: 3, NextLevel: &LevelMessage{ - Depth: 3, - MapDepth: 3, FD: fd[*protoconf.Section]("section_item_list"), - NextLevel: &LevelMessage{ - Depth: 4, - MapDepth: 3, - FD: fd[*protoconf.Section_SectionItem]("decompose_item_list"), - Indexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"SectionItemID"}, - Name: "Award", - }, - MD: md[*protoconf.Section_SectionItem](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.Section_SectionItem]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.Section_SectionItem]("id"), - }, + Depth: 4, + MapDepth: 3, + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"SectionItemID"}, + Name: "Award", + }, + MD: md[*protoconf.Section_SectionItem](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Section_SectionItem]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Section_SectionItem]("id"), }, }, }, }, - NextLevel: &LevelMessage{ - Depth: 5, - MapDepth: 3, - }, + }, + NextLevel: &LevelMessage{ + FD: fd[*protoconf.Section_SectionItem]("decompose_item_list"), + Depth: 5, + MapDepth: 3, }, }, }, @@ -455,132 +443,128 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 0, - MapDepth: 0, FD: fd[*protoconf.TaskConf]("task_map"), - NextLevel: &LevelMessage{ - Depth: 1, - MapDepth: 1, - Indexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"ActivityID"}, - SortedCols: []string{"Goal", "ID"}, - Name: "", - }, - MD: md[*protoconf.TaskConf_Task](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.TaskConf_Task]("activity_id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("activity_id"), - }, + Depth: 1, + MapDepth: 1, + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"ActivityID"}, + SortedCols: []string{"Goal", "ID"}, + Name: "", + }, + MD: md[*protoconf.TaskConf_Task](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("activity_id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("activity_id"), }, }, - SortedColFields: []*LevelField{ - { - FD: fd[*protoconf.TaskConf_Task]("goal"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("goal"), - }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("goal"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("goal"), }, - { - FD: fd[*protoconf.TaskConf_Task]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("id"), - }, + }, + { + FD: fd[*protoconf.TaskConf_Task]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("id"), }, }, }, }, - OrderedIndexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"Goal"}, - SortedCols: []string{"ID"}, - Name: "OrderedTask", - }, - MD: md[*protoconf.TaskConf_Task](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.TaskConf_Task]("goal"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("goal"), - }, + }, + OrderedIndexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"Goal"}, + SortedCols: []string{"ID"}, + Name: "OrderedTask", + }, + MD: md[*protoconf.TaskConf_Task](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("goal"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("goal"), }, }, - SortedColFields: []*LevelField{ - { - FD: fd[*protoconf.TaskConf_Task]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("id"), - }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("id"), }, }, }, - { - Index: &Index{ - Cols: []string{"Expiry"}, - Name: "TaskExpiry", - }, - MD: md[*protoconf.TaskConf_Task](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.TaskConf_Task]("expiry"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("expiry"), - }, + }, + { + Index: &Index{ + Cols: []string{"Expiry"}, + Name: "TaskExpiry", + }, + MD: md[*protoconf.TaskConf_Task](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("expiry"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("expiry"), }, }, }, - { - Index: &Index{ - Cols: []string{"Expiry"}, - SortedCols: []string{"Goal", "ID"}, - Name: "SortedTaskExpiry", - }, - MD: md[*protoconf.TaskConf_Task](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.TaskConf_Task]("expiry"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("expiry"), - }, + }, + { + Index: &Index{ + Cols: []string{"Expiry"}, + SortedCols: []string{"Goal", "ID"}, + Name: "SortedTaskExpiry", + }, + MD: md[*protoconf.TaskConf_Task](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("expiry"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("expiry"), }, }, - SortedColFields: []*LevelField{ - { - FD: fd[*protoconf.TaskConf_Task]("goal"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("goal"), - }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("goal"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("goal"), }, - { - FD: fd[*protoconf.TaskConf_Task]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("id"), - }, + }, + { + FD: fd[*protoconf.TaskConf_Task]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("id"), }, }, }, - { - Index: &Index{ - Cols: []string{"Expiry", "ActivityID"}, - Name: "ActivityExpiry", - }, - MD: md[*protoconf.TaskConf_Task](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.TaskConf_Task]("expiry"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("expiry"), - }, + }, + { + Index: &Index{ + Cols: []string{"Expiry", "ActivityID"}, + Name: "ActivityExpiry", + }, + MD: md[*protoconf.TaskConf_Task](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.TaskConf_Task]("expiry"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("expiry"), }, - { - FD: fd[*protoconf.TaskConf_Task]("activity_id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.TaskConf_Task]("activity_id"), - }, + }, + { + FD: fd[*protoconf.TaskConf_Task]("activity_id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.TaskConf_Task]("activity_id"), }, }, }, @@ -599,80 +583,76 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 0, - MapDepth: 0, FD: fd[*protoconf.Fruit3Conf]("fruit_list"), + Depth: 1, + MapDepth: 0, NextLevel: &LevelMessage{ - Depth: 1, - MapDepth: 0, FD: fd[*protoconf.Fruit3Conf_Fruit]("country_list"), + Depth: 2, + MapDepth: 0, + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"CountryName"}, + Name: "", + }, + MD: md[*protoconf.Fruit3Conf_Fruit_Country](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Fruit3Conf_Fruit_Country]("name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Fruit3Conf_Fruit_Country]("name"), + }, + }, + }, + }, + }, NextLevel: &LevelMessage{ - Depth: 2, - MapDepth: 0, FD: fd[*protoconf.Fruit3Conf_Fruit_Country]("item_map"), - Indexes: []*LevelIndex{ + Depth: 3, + MapDepth: 1, + OrderedIndexes: []*LevelIndex{ { Index: &Index{ - Cols: []string{"CountryName"}, - Name: "", + Cols: []string{"CountryItemPrice"}, + SortedCols: []string{"CountryItemID"}, + Name: "", }, - MD: md[*protoconf.Fruit3Conf_Fruit_Country](), + MD: md[*protoconf.Fruit3Conf_Fruit_Country_Item](), ColFields: []*LevelField{ { - FD: fd[*protoconf.Fruit3Conf_Fruit_Country]("name"), + FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("price"), LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.Fruit3Conf_Fruit_Country]("name"), + fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("price"), + }, + }, + }, + SortedColFields: []*LevelField{ + { + FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("id"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("id"), }, }, }, }, }, NextLevel: &LevelMessage{ - Depth: 3, - MapDepth: 1, FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("attr_list"), - OrderedIndexes: []*LevelIndex{ + Depth: 4, + MapDepth: 1, + Indexes: []*LevelIndex{ { Index: &Index{ - Cols: []string{"CountryItemPrice"}, - SortedCols: []string{"CountryItemID"}, - Name: "", + Cols: []string{"CountryItemAttrName"}, + Name: "", }, - MD: md[*protoconf.Fruit3Conf_Fruit_Country_Item](), + MD: md[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr](), ColFields: []*LevelField{ { - FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("price"), + FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr]("name"), LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("price"), - }, - }, - }, - SortedColFields: []*LevelField{ - { - FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("id"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.Fruit3Conf_Fruit_Country_Item]("id"), - }, - }, - }, - }, - }, - NextLevel: &LevelMessage{ - Depth: 4, - MapDepth: 1, - Indexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"CountryItemAttrName"}, - Name: "", - }, - MD: md[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr]("name"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr]("name"), - }, + fd[*protoconf.Fruit3Conf_Fruit_Country_Item_Attr]("name"), }, }, }, @@ -697,38 +677,34 @@ func Test_ParseIndexDescriptor(t *testing.T) { }, want: &IndexDescriptor{ LevelMessage: &LevelMessage{ - Depth: 0, - MapDepth: 0, FD: fd[*protoconf.Fruit5Conf]("fruit_map"), + Depth: 1, + MapDepth: 1, NextLevel: &LevelMessage{ - Depth: 1, - MapDepth: 1, FD: fd[*protoconf.Fruit5Conf_Fruit]("country_map"), - NextLevel: &LevelMessage{ - Depth: 2, - MapDepth: 2, - FD: fd[*protoconf.Fruit5Conf_Fruit_Country]("item_map"), - Indexes: []*LevelIndex{ - { - Index: &Index{ - Cols: []string{"CountryName"}, - Name: "", - }, - MD: md[*protoconf.Fruit5Conf_Fruit_Country](), - ColFields: []*LevelField{ - { - FD: fd[*protoconf.Fruit5Conf_Fruit_Country]("name"), - LeveledFDList: []protoreflect.FieldDescriptor{ - fd[*protoconf.Fruit5Conf_Fruit_Country]("name"), - }, + Depth: 2, + MapDepth: 2, + Indexes: []*LevelIndex{ + { + Index: &Index{ + Cols: []string{"CountryName"}, + Name: "", + }, + MD: md[*protoconf.Fruit5Conf_Fruit_Country](), + ColFields: []*LevelField{ + { + FD: fd[*protoconf.Fruit5Conf_Fruit_Country]("name"), + LeveledFDList: []protoreflect.FieldDescriptor{ + fd[*protoconf.Fruit5Conf_Fruit_Country]("name"), }, }, }, }, - NextLevel: &LevelMessage{ - Depth: 3, - MapDepth: 3, - }, + }, + NextLevel: &LevelMessage{ + FD: fd[*protoconf.Fruit5Conf_Fruit_Country]("item_map"), + Depth: 3, + MapDepth: 3, }, }, }, @@ -742,50 +718,3 @@ func Test_ParseIndexDescriptor(t *testing.T) { }) } } - -// Test_Fruit5Conf_MapKeysCollection verifies that initLevelMessage only collects -// map keys for levels whose deeper levels have indexes. -// -// Fruit5Conf has a 3-level map (fruit_map -> country_map -> item_map) but -// indexes only at MapDepth=2 (Country). initLevelMessage skips item_map -// because its NextLevel has no indexes, so len(keys) == 2, and the loop -// `for i := 0; i < len(keys)-2` produces 0 iterations — no extra -// LevelIndex key structs are generated. -func Test_Fruit5Conf_MapKeysCollection(t *testing.T) { - descriptor := ParseIndexDescriptor(md[*protoconf.Fruit5Conf]()) - - // Simulate the initLevelMessage logic: only collect map keys - // when the next level has any index (NeedGenAnyIndex). - var collectedMapKeyCount int - var totalMapKeyCount int - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - if fd := levelMessage.FD; fd != nil && fd.IsMap() { - totalMapKeyCount++ - if levelMessage.NextLevel.NeedGenAnyIndex() { - collectedMapKeyCount++ - } - } - } - - // Fruit5Conf: 3 total map levels, but only 2 collected (item_map is excluded). - assert.Equal(t, 3, totalMapKeyCount, "Fruit5Conf should have 3 total map levels") - assert.Equal(t, 2, collectedMapKeyCount, - "Only 2 map keys should be collected (item_map excluded because its NextLevel has no indexes)") - - // Verify the LevelIndex key generation loop produces 0 iterations. - levelIndexKeyCount := 0 - for i := 0; i < collectedMapKeyCount-2; i++ { - levelIndexKeyCount++ - } - assert.Equal(t, 0, levelIndexKeyCount, - "No LevelIndex key structs should be generated: len(keys)-2=%d", collectedMapKeyCount-2) - - // Contrast: if all 3 map fds were collected without filtering, - // the loop would incorrectly generate 1 extra LevelIndex key struct. - wrongKeyCount := 0 - for i := 0; i < totalMapKeyCount-2; i++ { - wrongKeyCount++ - } - assert.Equal(t, 1, wrongKeyCount, - "Without filtering, %d extra LevelIndex key struct(s) would be generated", wrongKeyCount) -} From bec0b660be26a1b965c8842364e2b5e9f340c0df Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 12 Mar 2026 21:22:39 +0800 Subject: [PATCH 24/32] refactor: optimize key collection logic in level message generation --- cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go | 10 +++++----- cmd/protoc-gen-go-tableau-loader/indexes/generator.go | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index 9c8c1d0f..17396023 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -34,7 +34,7 @@ func (x *Generator) initLevelMessage() { // Only collect map keys/fds when a deeper level has an index or ordered index, // because these keys are used solely for building upper-level (leveled) containers. if !lm.NextLevel.NeedGenAnyIndex() { - continue + break } x.keys = x.keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), @@ -100,16 +100,16 @@ func (x *Generator) GenHppIndexFinders() { // and keys[2]=k3: {k1,k2,k3} // // Hence the loop runs len(x.keys)-2 times (0 times when len ≤ 2). - for i := 0; i < len(x.keys)-2; i++ { - if i == 0 { + for i := 2; i < len(x.keys); i++ { + if i == 2 { x.g.P() x.g.P(helper.Indent(1), "// LevelIndex keys.") x.g.P(" public:") } - fd := x.keys[i+1].Fd + fd := x.keys[i-1].Fd keyType := x.levelKeyType(fd) x.g.P(helper.Indent(1), "struct ", keyType, " {") - keys := x.keys[:i+2] + keys := x.keys[:i] for _, key := range keys { x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") } diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index 6c6b3697..ee56cc42 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -36,7 +36,7 @@ func (x *Generator) initLevelMessage() { // Only collect map keys/fds when a deeper level has an index or ordered index, // because these keys are used solely for building upper-level (leveled) containers. if !lm.NextLevel.NeedGenAnyIndex() { - continue + break } x.keys = x.keys.AddMapKey(helper.MapKey{ Type: helper.ParseMapKeyType(fd.MapKey()), @@ -103,14 +103,14 @@ func (x *Generator) GenIndexTypeDef() { // and keys[2]=k3: {k1,k2,k3} // // Hence the loop runs len(x.keys)-2 times (0 times when len ≤ 2). - for i := 0; i < len(x.keys)-2; i++ { - if i == 0 { + for i := 2; i < len(x.keys); i++ { + if i == 2 { x.g.P() x.g.P("// LevelIndex keys.") } - fd := x.keys[i+1].Fd + fd := x.keys[i-1].Fd keyType := x.levelKeyType(fd) - keys := x.keys[:i+2] + keys := x.keys[:i] x.g.P("type ", keyType, " struct {") for _, key := range keys { x.g.P(key.FieldName, " ", key.Type) From ae16348c6c7efcd6be8b534be1c4d843232d72be Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 12 Mar 2026 21:47:15 +0800 Subject: [PATCH 25/32] refactor: clarify comments on LevelIndex key struct generation logic --- .../indexes/generator.go | 20 +++++++++---------- .../indexes/generator.go | 20 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go index 17396023..c5031c33 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-cpp-tableau-loader/indexes/generator.go @@ -86,20 +86,20 @@ func (x *Generator) GenHppIndexFinders() { } // Generate LevelIndex key structs for intermediate map levels. // - // x.keys holds one entry per effective map level (only levels that lead to - // an index are included by initLevelMessage). For a 3-level map keyed by + // x.keys holds one entry per map level whose next level still needs an + // index (populated by initLevelMessage). For a 3-level map keyed by // (k1, k2, k3) with an index at the deepest level, x.keys = [k1, k2, k3]. // - // The deepest level's key (k3) is the direct lookup key and does not need - // a LevelIndex struct. The top-level key (k1, i.e. keys[0]) is a plain map - // key and also does not need one. Only the intermediate levels (keys[1] … - // keys[len-2]) require a LevelIndex struct that bundles all ancestor keys: + // Level containers at depth 1 are keyed by a single scalar (k1), so no + // composite key struct is needed. Only depths ≥ 2 require a LevelIndex + // struct that bundles all ancestor keys up to that depth: // - // keys = [k1, k2, k3] → one struct for keys[1]=k2: { k1, k2 } - // keys = [k1,k2,k3,k4] → structs for keys[1]=k2: {k1,k2} - // and keys[2]=k3: {k1,k2,k3} + // keys = [k1, k2, k3] → struct for depth 2: {k1, k2} + // keys = [k1, k2, k3, k4] → struct for depth 2: {k1, k2} + // struct for depth 3: {k1, k2, k3} // - // Hence the loop runs len(x.keys)-2 times (0 times when len ≤ 2). + // The loop starts at i=2 (depth 2) and creates a struct from keys[:i]. + // It runs len(x.keys)-2 times (0 times when len ≤ 2). for i := 2; i < len(x.keys); i++ { if i == 2 { x.g.P() diff --git a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go index ee56cc42..a09671ea 100644 --- a/cmd/protoc-gen-go-tableau-loader/indexes/generator.go +++ b/cmd/protoc-gen-go-tableau-loader/indexes/generator.go @@ -89,20 +89,20 @@ func (x *Generator) GenIndexTypeDef() { } // Generate LevelIndex key structs for intermediate map levels. // - // x.keys holds one entry per effective map level (only levels that lead to - // an index are included by initLevelMessage). For a 3-level map keyed by + // x.keys holds one entry per map level whose next level still needs an + // index (populated by initLevelMessage). For a 3-level map keyed by // (k1, k2, k3) with an index at the deepest level, x.keys = [k1, k2, k3]. // - // The deepest level's key (k3) is the direct lookup key and does not need - // a LevelIndex struct. The top-level key (k1, i.e. keys[0]) is a plain map - // key and also does not need one. Only the intermediate levels (keys[1] … - // keys[len-2]) require a LevelIndex struct that bundles all ancestor keys: + // Level containers at depth 1 are keyed by a single scalar (k1), so no + // composite key struct is needed. Only depths ≥ 2 require a LevelIndex + // struct that bundles all ancestor keys up to that depth: // - // keys = [k1, k2, k3] → one struct for keys[1]=k2: { k1, k2 } - // keys = [k1,k2,k3,k4] → structs for keys[1]=k2: {k1,k2} - // and keys[2]=k3: {k1,k2,k3} + // keys = [k1, k2, k3] → struct for depth 2: {k1, k2} + // keys = [k1, k2, k3, k4] → struct for depth 2: {k1, k2} + // struct for depth 3: {k1, k2, k3} // - // Hence the loop runs len(x.keys)-2 times (0 times when len ≤ 2). + // The loop starts at i=2 (depth 2) and creates a struct from keys[:i]. + // It runs len(x.keys)-2 times (0 times when len ≤ 2). for i := 2; i < len(x.keys); i++ { if i == 2 { x.g.P() From df38df912103768344b28859823f36d2a8732265 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 21:55:11 +0800 Subject: [PATCH 26/32] fix: workflow --- .github/workflows/testing-cpp.yml | 21 +++++++++++++-------- init.bat | 5 +---- init.sh | 3 +-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index a9d578bc..f67dde45 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -39,25 +39,30 @@ jobs: go-version: 1.21.x cache: true + - name: Install dependencies (Ubuntu) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y cmake libprotobuf-dev protobuf-compiler + - name: Install CMake (Windows) if: runner.os == 'Windows' uses: jwlawson/actions-setup-cmake@v2 with: cmake-version: "3.x" - - name: Install dependencies (Ubuntu) - if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install -y cmake libprotobuf-dev protobuf-compiler - - - name: Setup MSVC (Windows only) + - name: Setup MSVC (Windows) if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 - - name: Install protobuf (Windows) + - name: Install Ninja (Windows) if: runner.os == 'Windows' - run: choco install protoc + run: choco install ninja - - name: Init submodules + - name: Init submodules and build protobuf (Windows) + if: runner.os == 'Windows' + run: bash init.sh + + - name: Init submodules (Ubuntu) + if: runner.os == 'Linux' run: git submodule update --init --recursive - name: Generate protoconf diff --git a/init.bat b/init.bat index f1921aa4..bc078e3d 100644 --- a/init.bat +++ b/init.bat @@ -15,10 +15,7 @@ REM Build and install the C++ Protocol Buffer runtime and the Protocol Buffer co REM Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README.md#cmake-configuration cd cmake REM use Debug version -cmake -S . -B build ^ --G "Ninja" ^ --DCMAKE_BUILD_TYPE=Debug ^ --DCMAKE_POLICY_VERSION_MINIMUM="3.5" +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF REM Compile the code cmake --build build --parallel diff --git a/init.sh b/init.sh index 4844fb2e..f29fd54f 100755 --- a/init.sh +++ b/init.sh @@ -22,8 +22,7 @@ git submodule update --init --recursive # Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README.md#cmake-configuration cd cmake # use Debug version -cmake -S . -B build \ - -DCMAKE_BUILD_TYPE=Debug +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF # Compile the code cmake --build build --parallel From 9b420fe4d144030dd15b826e74df58444c994463 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 22:34:39 +0800 Subject: [PATCH 27/32] fix: workflow --- .github/workflows/testing-cpp.yml | 10 +++------- test/cpp-tableau-loader/CMakeLists.txt | 8 ++++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index f67dde45..18440a7e 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -41,7 +41,7 @@ jobs: - name: Install dependencies (Ubuntu) if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install -y cmake libprotobuf-dev protobuf-compiler + run: sudo apt-get update && sudo apt-get install -y cmake ninja-build - name: Install CMake (Windows) if: runner.os == 'Windows' @@ -57,14 +57,10 @@ jobs: if: runner.os == 'Windows' run: choco install ninja - - name: Init submodules and build protobuf (Windows) - if: runner.os == 'Windows' + - name: Init submodules and build protobuf + shell: bash run: bash init.sh - - name: Init submodules (Ubuntu) - if: runner.os == 'Linux' - run: git submodule update --init --recursive - - name: Generate protoconf working-directory: test/cpp-tableau-loader run: ${{ matrix.gen_script }} diff --git a/test/cpp-tableau-loader/CMakeLists.txt b/test/cpp-tableau-loader/CMakeLists.txt index fb811ac2..002fc3ca 100644 --- a/test/cpp-tableau-loader/CMakeLists.txt +++ b/test/cpp-tableau-loader/CMakeLists.txt @@ -31,10 +31,18 @@ if(EXISTS "${LOCAL_PROTOBUF_BUILD_DIR}" AND EXISTS "${LOCAL_PROTOBUF_SRC_DIR}") message(STATUS "Found local protobuf submodule, using it preferentially.") list(PREPEND CMAKE_PREFIX_PATH "${LOCAL_PROTOBUF_BUILD_DIR}") set(Protobuf_INCLUDE_DIR "${LOCAL_PROTOBUF_SRC_DIR}" CACHE PATH "Protobuf include dir" FORCE) + # Use static protobuf to avoid dynamic linking issues across all platforms. + set(Protobuf_USE_STATIC_LIBS ON) endif() find_package(Protobuf REQUIRED) message(STATUS "Using protobuf ${Protobuf_VERSION}") +# When linking protobuf statically, PROTOBUF_USE_DLLS must not be defined, +# otherwise symbols will be decorated with __declspec(dllimport) and cause LNK2019. +if(Protobuf_USE_STATIC_LIBS) + remove_definitions(-DPROTOBUF_USE_DLLS) +endif() + # loader SET(LOADER_SRC_DIR ${PROJECT_SOURCE_DIR}/src/) From a30d923323c1809cdb6d971068b0aeb77e7abb5d Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 23:08:17 +0800 Subject: [PATCH 28/32] fix(protobuf): disable MSVC static runtime to fix RuntimeLibrary mismatch on Windows --- init.bat | 3 ++- init.sh | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/init.bat b/init.bat index bc078e3d..184dad7f 100644 --- a/init.bat +++ b/init.bat @@ -15,7 +15,8 @@ REM Build and install the C++ Protocol Buffer runtime and the Protocol Buffer co REM Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README.md#cmake-configuration cd cmake REM use Debug version -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF +REM -Dprotobuf_MSVC_STATIC_RUNTIME=OFF: use dynamic CRT (/MDd) to match the default MSVC runtime library setting. +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF REM Compile the code cmake --build build --parallel diff --git a/init.sh b/init.sh index f29fd54f..6bcdf39a 100755 --- a/init.sh +++ b/init.sh @@ -22,7 +22,9 @@ git submodule update --init --recursive # Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README.md#cmake-configuration cd cmake # use Debug version -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF +# -Dprotobuf_MSVC_STATIC_RUNTIME=OFF: use dynamic CRT (/MDd) to match the default MSVC runtime library setting. +# This flag is only effective on Windows/MSVC and is ignored on other platforms. +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF # Compile the code cmake --build build --parallel From 7636003e3098da643dfac3bbe00c68d7c6396759 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 23:16:54 +0800 Subject: [PATCH 29/32] fix(windows): force dynamic CRT via CMAKE_MSVC_RUNTIME_LIBRARY to fix LNK2038 mismatch --- .github/workflows/testing-cpp.yml | 2 +- init.bat | 3 ++- init.sh | 5 +++-- test/cpp-tableau-loader/CMakeLists.txt | 2 ++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index 18440a7e..35eadc82 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -23,7 +23,7 @@ jobs: run_loader: ./bin/loader - os: windows-latest gen_script: cmd /c gen.bat - cmake_config: cmake -S . -B build -G "Ninja" + cmake_config: cmake -S . -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Debug run_loader: .\bin\loader.exe runs-on: ${{ matrix.os }} diff --git a/init.bat b/init.bat index 184dad7f..e519559e 100644 --- a/init.bat +++ b/init.bat @@ -16,7 +16,8 @@ REM Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README. cd cmake REM use Debug version REM -Dprotobuf_MSVC_STATIC_RUNTIME=OFF: use dynamic CRT (/MDd) to match the default MSVC runtime library setting. -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF +REM -DCMAKE_MSVC_RUNTIME_LIBRARY: CMake 3.15+ standard way to control MSVC runtime library. +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" REM Compile the code cmake --build build --parallel diff --git a/init.sh b/init.sh index 6bcdf39a..565bf96b 100755 --- a/init.sh +++ b/init.sh @@ -23,8 +23,9 @@ git submodule update --init --recursive cd cmake # use Debug version # -Dprotobuf_MSVC_STATIC_RUNTIME=OFF: use dynamic CRT (/MDd) to match the default MSVC runtime library setting. -# This flag is only effective on Windows/MSVC and is ignored on other platforms. -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF +# -DCMAKE_MSVC_RUNTIME_LIBRARY: CMake 3.15+ standard way to control MSVC runtime library. +# These flags are only effective on Windows/MSVC and are ignored on other platforms. +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" # Compile the code cmake --build build --parallel diff --git a/test/cpp-tableau-loader/CMakeLists.txt b/test/cpp-tableau-loader/CMakeLists.txt index 002fc3ca..6591b971 100644 --- a/test/cpp-tableau-loader/CMakeLists.txt +++ b/test/cpp-tableau-loader/CMakeLists.txt @@ -19,6 +19,8 @@ message(STATUS "Using C++${CMAKE_CXX_STANDARD} standard") if (MSVC) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /DNDEBUG") + # Use dynamic CRT (/MD or /MDd) to match protobuf's build configuration. + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") else() SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -fPIC -Wno-deprecated -Wno-unused-variable -Wno-sign-compare -Wno-strict-aliasing -fno-strict-aliasing -DNDEBUG") endif() From fbaf70dd93641caa471c550175ead4b111d3834a Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 23:23:45 +0800 Subject: [PATCH 30/32] fix(windows): use static CRT (/MTd) everywhere to match protobuf default static runtime --- init.bat | 6 +++--- init.sh | 7 +++---- test/cpp-tableau-loader/CMakeLists.txt | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/init.bat b/init.bat index e519559e..b4241dde 100644 --- a/init.bat +++ b/init.bat @@ -15,9 +15,9 @@ REM Build and install the C++ Protocol Buffer runtime and the Protocol Buffer co REM Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README.md#cmake-configuration cd cmake REM use Debug version -REM -Dprotobuf_MSVC_STATIC_RUNTIME=OFF: use dynamic CRT (/MDd) to match the default MSVC runtime library setting. -REM -DCMAKE_MSVC_RUNTIME_LIBRARY: CMake 3.15+ standard way to control MSVC runtime library. -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" +REM protobuf_MSVC_STATIC_RUNTIME defaults to ON, which uses static CRT (/MTd for Debug). +REM Our project's CMakeLists.txt also sets static CRT to match. +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF REM Compile the code cmake --build build --parallel diff --git a/init.sh b/init.sh index 565bf96b..57d6c505 100755 --- a/init.sh +++ b/init.sh @@ -22,10 +22,9 @@ git submodule update --init --recursive # Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README.md#cmake-configuration cd cmake # use Debug version -# -Dprotobuf_MSVC_STATIC_RUNTIME=OFF: use dynamic CRT (/MDd) to match the default MSVC runtime library setting. -# -DCMAKE_MSVC_RUNTIME_LIBRARY: CMake 3.15+ standard way to control MSVC runtime library. -# These flags are only effective on Windows/MSVC and are ignored on other platforms. -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" +# protobuf_MSVC_STATIC_RUNTIME defaults to ON, which uses static CRT (/MTd for Debug). +# Our project's CMakeLists.txt also sets static CRT to match. +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF # Compile the code cmake --build build --parallel diff --git a/test/cpp-tableau-loader/CMakeLists.txt b/test/cpp-tableau-loader/CMakeLists.txt index 6591b971..036ba848 100644 --- a/test/cpp-tableau-loader/CMakeLists.txt +++ b/test/cpp-tableau-loader/CMakeLists.txt @@ -19,8 +19,8 @@ message(STATUS "Using C++${CMAKE_CXX_STANDARD} standard") if (MSVC) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /DNDEBUG") - # Use dynamic CRT (/MD or /MDd) to match protobuf's build configuration. - set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + # Use static CRT (/MT or /MTd) to match protobuf's default static runtime build. + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") else() SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -fPIC -Wno-deprecated -Wno-unused-variable -Wno-sign-compare -Wno-strict-aliasing -fno-strict-aliasing -DNDEBUG") endif() From ce723ff2b00704b1e13ac50a8e751a73ad41dca9 Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 23:40:30 +0800 Subject: [PATCH 31/32] fix: use find_package CONFIG mode and disable ZLIB for robust cross-platform protobuf linking Key changes: - Use find_package(Protobuf CONFIG REQUIRED) instead of MODULE mode to correctly pick up protobuf's own config files from the local build directory. - Disable ZLIB dependency (-Dprotobuf_WITH_ZLIB=OFF) in protobuf build to avoid ZLIB::ZLIB link requirement in exported CMake targets. - Explicitly set -Dprotobuf_BUILD_SHARED_LIBS=OFF for clarity. - Use Ninja generator and Debug build type consistently on both Ubuntu and Windows. - protobuf::libprotobuf imported target auto-provides include dirs, so removed manual Protobuf_INCLUDE_DIRS from include_directories(). - Static CRT (/MTd) on MSVC matches protobuf's default static runtime. --- .github/workflows/testing-cpp.yml | 2 +- init.bat | 9 +++++--- init.sh | 9 +++++--- test/cpp-tableau-loader/CMakeLists.txt | 30 +++++++++++++------------- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index 35eadc82..8e2f669c 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -19,7 +19,7 @@ jobs: include: - os: ubuntu-latest gen_script: bash gen.sh - cmake_config: cmake -S . -B build + cmake_config: cmake -S . -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Debug run_loader: ./bin/loader - os: windows-latest gen_script: cmd /c gen.bat diff --git a/init.bat b/init.bat index b4241dde..6d8d421b 100644 --- a/init.bat +++ b/init.bat @@ -15,9 +15,12 @@ REM Build and install the C++ Protocol Buffer runtime and the Protocol Buffer co REM Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README.md#cmake-configuration cd cmake REM use Debug version -REM protobuf_MSVC_STATIC_RUNTIME defaults to ON, which uses static CRT (/MTd for Debug). -REM Our project's CMakeLists.txt also sets static CRT to match. -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF +REM - protobuf_MSVC_STATIC_RUNTIME defaults to ON, which uses static CRT (/MTd for Debug). +REM Our project's CMakeLists.txt also sets static CRT to match. +REM - protobuf_WITH_ZLIB=OFF: disable ZLIB dependency to avoid ZLIB::ZLIB link requirement +REM in protobuf's exported CMake targets, which simplifies cross-platform builds. +REM - protobuf_BUILD_SHARED_LIBS=OFF: build static libraries explicitly. +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_BUILD_SHARED_LIBS=OFF REM Compile the code cmake --build build --parallel diff --git a/init.sh b/init.sh index 57d6c505..7b4ee9d1 100755 --- a/init.sh +++ b/init.sh @@ -22,9 +22,12 @@ git submodule update --init --recursive # Refer: https://github.com/protocolbuffers/protobuf/blob/3.19.x/cmake/README.md#cmake-configuration cd cmake # use Debug version -# protobuf_MSVC_STATIC_RUNTIME defaults to ON, which uses static CRT (/MTd for Debug). -# Our project's CMakeLists.txt also sets static CRT to match. -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF +# - protobuf_MSVC_STATIC_RUNTIME defaults to ON, which uses static CRT (/MTd for Debug). +# Our project's CMakeLists.txt also sets static CRT to match. +# - protobuf_WITH_ZLIB=OFF: disable ZLIB dependency to avoid ZLIB::ZLIB link requirement +# in protobuf's exported CMake targets, which simplifies cross-platform builds. +# - protobuf_BUILD_SHARED_LIBS=OFF: build static libraries explicitly. +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_BUILD_SHARED_LIBS=OFF # Compile the code cmake --build build --parallel diff --git a/test/cpp-tableau-loader/CMakeLists.txt b/test/cpp-tableau-loader/CMakeLists.txt index 036ba848..0297c3e1 100644 --- a/test/cpp-tableau-loader/CMakeLists.txt +++ b/test/cpp-tableau-loader/CMakeLists.txt @@ -27,37 +27,37 @@ endif() # google protobuf # Try to find protobuf from local submodule first, then fallback to system. +# Protobuf's install.cmake generates config files under: +# - Linux: build/lib64/cmake/protobuf/ (via CMAKE_INSTALL_LIBDIR) +# - Windows: build/cmake/ (MSVC uses "cmake" directly) +# Setting CMAKE_PREFIX_PATH to the build dir allows CMake to search both layouts. set(LOCAL_PROTOBUF_BUILD_DIR "${PROJECT_SOURCE_DIR}/../../third_party/_submodules/protobuf/cmake/build") set(LOCAL_PROTOBUF_SRC_DIR "${PROJECT_SOURCE_DIR}/../../third_party/_submodules/protobuf/src") if(EXISTS "${LOCAL_PROTOBUF_BUILD_DIR}" AND EXISTS "${LOCAL_PROTOBUF_SRC_DIR}") message(STATUS "Found local protobuf submodule, using it preferentially.") list(PREPEND CMAKE_PREFIX_PATH "${LOCAL_PROTOBUF_BUILD_DIR}") - set(Protobuf_INCLUDE_DIR "${LOCAL_PROTOBUF_SRC_DIR}" CACHE PATH "Protobuf include dir" FORCE) - # Use static protobuf to avoid dynamic linking issues across all platforms. - set(Protobuf_USE_STATIC_LIBS ON) endif() -find_package(Protobuf REQUIRED) -message(STATUS "Using protobuf ${Protobuf_VERSION}") -# When linking protobuf statically, PROTOBUF_USE_DLLS must not be defined, -# otherwise symbols will be decorated with __declspec(dllimport) and cause LNK2019. -if(Protobuf_USE_STATIC_LIBS) - remove_definitions(-DPROTOBUF_USE_DLLS) -endif() +# Use CONFIG mode explicitly to pick up protobuf's own protobuf-config.cmake +# instead of CMake's built-in FindProtobuf.cmake module, which may not handle +# the local build directory layout correctly. +find_package(Protobuf CONFIG REQUIRED) +message(STATUS "Using protobuf ${Protobuf_VERSION}") # loader SET(LOADER_SRC_DIR ${PROJECT_SOURCE_DIR}/src/) # include -include_directories(${Protobuf_INCLUDE_DIRS} ${LOADER_SRC_DIR} ${LOADER_SRC_DIR}/protoconf) +# protobuf::libprotobuf target already provides its include directories via +# INTERFACE_INCLUDE_DIRECTORIES, so we only need to add our own source dirs. +include_directories(${LOADER_SRC_DIR} ${LOADER_SRC_DIR}/protoconf) # add the executable add_executable(${PROJECT_NAME} ${PROTO_SOURCE} ${SOURCE}) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # link libraries -if(MSVC) - target_link_libraries(${PROJECT_NAME} protobuf::libprotobuf) -else() - target_link_libraries(${PROJECT_NAME} protobuf::libprotobuf pthread stdc++fs) +target_link_libraries(${PROJECT_NAME} protobuf::libprotobuf) +if(NOT MSVC) + target_link_libraries(${PROJECT_NAME} pthread stdc++fs) endif() From 14f417e5fdd3ad2a24747a7591be3dd46944510f Mon Sep 17 00:00:00 2001 From: wenchy Date: Thu, 12 Mar 2026 23:49:45 +0800 Subject: [PATCH 32/32] ci(windows): consolidate CMake and Ninja installation into single choco install step --- .github/workflows/testing-cpp.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/testing-cpp.yml b/.github/workflows/testing-cpp.yml index 8e2f669c..13ba5d61 100644 --- a/.github/workflows/testing-cpp.yml +++ b/.github/workflows/testing-cpp.yml @@ -43,20 +43,14 @@ jobs: if: runner.os == 'Linux' run: sudo apt-get update && sudo apt-get install -y cmake ninja-build - - name: Install CMake (Windows) + - name: Install dependencies (Windows) if: runner.os == 'Windows' - uses: jwlawson/actions-setup-cmake@v2 - with: - cmake-version: "3.x" + run: choco install cmake ninja -y - name: Setup MSVC (Windows) if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 - - name: Install Ninja (Windows) - if: runner.os == 'Windows' - run: choco install ninja - - name: Init submodules and build protobuf shell: bash run: bash init.sh