diff --git a/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go b/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go index 2536c7f2..e136cc36 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go +++ b/cmd/protoc-gen-cpp-tableau-loader/helper/helper.go @@ -40,11 +40,22 @@ func protocVersion(gen *protogen.Plugin) string { return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix) } +func ParseMapFieldName(fd protoreflect.FieldDescriptor) string { + opts := fd.Options().(*descriptorpb.FieldOptions) + fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) + name := fdOpts.GetKey() + if fd.MapValue().Kind() == protoreflect.MessageKind { + valueFd := fd.MapValue().Message().Fields().Get(0) + name = string(valueFd.Name()) + } + return escapeIdentifier(name) +} + func ParseIndexFieldName(fd protoreflect.FieldDescriptor) string { return escapeIdentifier(string(fd.Name())) } -func ParseIndexFieldNameAsKeyStructFieldName(fd protoreflect.FieldDescriptor) string { +func ParseIndexFieldNameAsFuncParam(fd protoreflect.FieldDescriptor) string { if fd.IsList() { opts := fd.Options().(*descriptorpb.FieldOptions) fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) @@ -53,10 +64,6 @@ func ParseIndexFieldNameAsKeyStructFieldName(fd protoreflect.FieldDescriptor) st return ParseIndexFieldName(fd) } -func ParseIndexFieldNameAsFuncParam(fd protoreflect.FieldDescriptor) string { - return ParseIndexFieldNameAsKeyStructFieldName(fd) -} - // ParseCppType converts a FieldDescriptor to C++ type string. func ParseCppType(fd protoreflect.FieldDescriptor) string { switch fd.Kind() { @@ -113,11 +120,11 @@ func ParseMapKeyType(fd protoreflect.FieldDescriptor) string { } } -// ParseOrderedMapKeyType converts a FieldDescriptor to its treemap key type. +// ParseOrderedIndexKeyType converts a FieldDescriptor to its treemap key type. // fd must be an ordered type, or a message which can be converted to an ordered type. -func ParseOrderedMapKeyType(fd protoreflect.FieldDescriptor) string { +func ParseOrderedIndexKeyType(fd protoreflect.FieldDescriptor) string { switch fd.Kind() { - case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.EnumKind: + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: return "int32_t" case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: return "uint32_t" @@ -131,6 +138,9 @@ func ParseOrderedMapKeyType(fd protoreflect.FieldDescriptor) string { return "double" case protoreflect.StringKind: return "std::string" + case protoreflect.EnumKind: + protoFullName := string(fd.Enum().FullName()) + return strings.ReplaceAll(protoFullName, ".", "::") case protoreflect.MessageKind: switch fd.Message().FullName() { case "google.protobuf.Timestamp", "google.protobuf.Duration": @@ -160,54 +170,58 @@ type MapKey struct { Name string } -func AddMapKey(fd protoreflect.FieldDescriptor, keys []MapKey) []MapKey { - opts := fd.Options().(*descriptorpb.FieldOptions) - fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) - name := fdOpts.GetKey() - if fd.MapValue().Kind() == protoreflect.MessageKind { - valueFd := fd.MapValue().Message().Fields().Get(0) - name = string(valueFd.Name()) - } - name = escapeIdentifier(name) - if name == "" { - name = fmt.Sprintf("key%d", len(keys)+1) +type MapKeys []MapKey + +func (keys MapKeys) AddMapKey(newKey MapKey) MapKeys { + if newKey.Name == "" { + newKey.Name = fmt.Sprintf("key%d", len(keys)+1) } else { for _, key := range keys { - if key.Name == name { + if key.Name == newKey.Name { // rewrite to avoid name confict - name = fmt.Sprintf("%s%d", name, len(keys)+1) + newKey.Name = fmt.Sprintf("%s%d", newKey.Name, len(keys)+1) break } } } - keys = append(keys, MapKey{ParseMapKeyType(fd.MapKey()), name}) - return keys + return append(keys, newKey) } // GenGetParams generates function parameters, which are the names listed in the function's definition. -func GenGetParams(keys []MapKey) string { - var params string - for i, key := range keys { - params += ToConstRefType(key.Type) + " " + key.Name - if i != len(keys)-1 { - params += ", " - } +func (keys MapKeys) GenGetParams() string { + var params []string + for _, key := range keys { + params = append(params, ToConstRefType(key.Type)+" "+key.Name) } - return params + return strings.Join(params, ", ") } // GenGetArguments generates function arguments, which are the real values passed to the function. -func GenGetArguments(keys []MapKey) string { - var params string - for i, key := range keys { - params += key.Name - if i != len(keys)-1 { - params += ", " - } +func (keys MapKeys) GenGetArguments() string { + var params []string + for _, key := range keys { + params = append(params, key.Name) + } + return strings.Join(params, ", ") +} + +// GenOtherArguments generates function arguments for other value of std::tie. +func (keys MapKeys) GenOtherArguments(other string) string { + var params []string + for _, key := range keys { + params = append(params, other+"."+key.Name) } - return params + return strings.Join(params, ", ") } func Indent(depth int) string { return strings.Repeat(" ", depth) } + +func ParseMapValueType(fd protoreflect.FieldDescriptor) string { + valueType := ParseCppType(fd.MapValue()) + if fd.MapValue().Kind() == protoreflect.MessageKind { + return "const " + valueType + "*" + } + return valueType +} diff --git a/cmd/protoc-gen-cpp-tableau-loader/index.go b/cmd/protoc-gen-cpp-tableau-loader/index.go deleted file mode 100644 index 8021f145..00000000 --- a/cmd/protoc-gen-cpp-tableau-loader/index.go +++ /dev/null @@ -1,291 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" - "google.golang.org/protobuf/compiler/protogen" -) - -func genHppIndexFinders(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - g.P(helper.Indent(1), "// Index accessers.") - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take first field - g.P(helper.Indent(1), "// Index: ", index.Index) - g.P(" public:") - vectorType := fmt.Sprintf("Index_%sVector", index.Name()) - mapType := fmt.Sprintf("Index_%sMap", index.Name()) - g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") - keyType := helper.ParseCppType(field.FD) - g.P(helper.Indent(1), "using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, ">;") - g.P(helper.Indent(1), "// Finds the index (", index.Index, ") to value (", vectorType, ") hash map.") - g.P(helper.Indent(1), "// One key may correspond to multiple values, which are contained by a vector.") - g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "Map() const;") - g.P(helper.Indent(1), "// Finds a vector of all values of the given key.") - g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", helper.ToConstRefType(keyType), " ", helper.ParseIndexFieldNameAsFuncParam(field.FD), ") const;") - g.P(helper.Indent(1), "// Finds the first value of the given key.") - g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", helper.ToConstRefType(keyType), " ", helper.ParseIndexFieldNameAsFuncParam(field.FD), ") const;") - g.P() - - g.P(" private:") - indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P(helper.Indent(1), mapType, " ", indexContainerName, ";") - g.P() - } else { - // multi-column index - g.P(helper.Indent(1), "// Index: ", index.Index) - g.P(" public:") - keyType := fmt.Sprintf("Index_%sKey", index.Name()) - keyHasherType := fmt.Sprintf("Index_%sKeyHasher", index.Name()) - vectorType := fmt.Sprintf("Index_%sVector", index.Name()) - mapType := fmt.Sprintf("Index_%sMap", index.Name()) - - // generate key struct - g.P(helper.Indent(1), "struct ", keyType, " {") - var keys []helper.MapKey - equality := "" - for i, field := range index.ColFields { - typ := helper.ParseCppType(field.FD) - keys = append(keys, helper.MapKey{ - Type: typ, - Name: helper.ParseIndexFieldNameAsFuncParam(field.FD), - }) - g.P(helper.Indent(2), typ, " ", helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD), ";") - equality += helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) + " == other." + helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) - if i != len(index.ColFields)-1 { - equality += " && " - } - } - g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const {") - g.P(helper.Indent(3), "return ", equality, ";") - g.P(helper.Indent(2), "}") - g.P(helper.Indent(1), "};") - - // generate key hasher struct - g.P(helper.Indent(1), "struct ", keyHasherType, " {") - combinedKeys := "" - for i, field := range index.ColFields { - key := "key." + helper.ParseIndexFieldNameAsKeyStructFieldName(field.FD) - combinedKeys += key - if i != len(index.ColFields)-1 { - combinedKeys += ", " - } - } - g.P(helper.Indent(2), "std::size_t operator()(const ", keyType, "& key) const {") - g.P(helper.Indent(3), "return util::SugaredHashCombine(", combinedKeys, ");") - g.P(helper.Indent(2), "}") - g.P(helper.Indent(1), "};") - - g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") - g.P(helper.Indent(1), "using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, ", ", keyHasherType, ">;") - g.P(helper.Indent(1), "// Finds the index (", index.Index, ") to value (", vectorType, ") hash map.") - g.P(helper.Indent(1), "// One key may correspond to multiple values, which are contained by a vector.") - g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "Map() const;") - g.P(helper.Indent(1), "// Finds a vector of all values of the given keys.") - g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", helper.GenGetParams(keys), ") const;") - g.P(helper.Indent(1), "// Finds the first value of the given keys.") - g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", helper.GenGetParams(keys), ") const;") - g.P() - - g.P(" private:") - indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P(helper.Indent(1), mapType, " ", indexContainerName, ";") - g.P() - } - } - } -} - -func genCppIndexLoader(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - g.P(helper.Indent(1), "// Index init.") - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P(helper.Indent(1), indexContainerName, ".clear();") - } - } - parentDataName := "data_" - depth := 1 - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - genOneCppIndexLoader(g, depth, index, parentDataName) - } - itemName := fmt.Sprintf("item%d", depth) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGen() { - break - } - g.P(helper.Indent(depth), "for (auto&& ", itemName, " : ", parentDataName, ".", helper.ParseIndexFieldName(levelMessage.FD), "()) {") - parentDataName = itemName - if levelMessage.FD.IsMap() { - parentDataName = itemName + ".second" - } - depth++ - } - for i := depth - 1; i > 0; i-- { - g.P(helper.Indent(i), "}") - } - genIndexSorter(g, descriptor) -} - -func genOneCppIndexLoader(g *protogen.GeneratedFile, depth int, index *index.LevelIndex, parentDataName string) { - indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P(helper.Indent(depth), "{") - g.P(helper.Indent(depth+1), "// Index: ", index.Index) - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take the first field - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" - } - g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") - key := itemName - if field.FD.Enum() != nil { - key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" - } - g.P(helper.Indent(depth+2), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") - g.P(helper.Indent(depth+1), "}") - } else { - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" - } - key := parentDataName + fieldName - g.P(helper.Indent(depth+1), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") - } - } else { - // multi-column index - generateOneCppMulticolumnIndex(g, depth, index, parentDataName, nil) - } - g.P(helper.Indent(depth), "}") -} - -func genIndexSorter(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" - if len(index.SortedColFields) != 0 { - g.P(helper.Indent(1), "// Index(sort): ", index.Index) - g.P(helper.Indent(1), "for (auto&& item : ", indexContainerName, ") {") - g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(),") - g.P(helper.Indent(7), "[](const ", helper.ParseCppClassType(index.MD), "* a, const ", helper.ParseCppClassType(index.MD), "* b) {") - for i, field := range index.SortedColFields { - fieldName := "" - for i, leveledFd := range field.LeveledFDList { - accessOperator := "." - if i == 0 { - accessOperator = "->" - } - fieldName += accessOperator + helper.ParseIndexFieldName(leveledFd) + "()" - } - if i == len(index.SortedColFields)-1 { - g.P(helper.Indent(8), "return a", fieldName, " < b", fieldName, ";") - } else { - g.P(helper.Indent(8), "if (a", fieldName, " != b", fieldName, ") {") - g.P(helper.Indent(9), "return a", fieldName, " < b", fieldName, ";") - g.P(helper.Indent(8), "}") - } - } - g.P(helper.Indent(7), "});") - g.P(helper.Indent(1), "}") - } - } - } -} - -func generateOneCppMulticolumnIndex(g *protogen.GeneratedFile, depth int, index *index.LevelIndex, parentDataName string, keys []string) []string { - cursor := len(keys) - if cursor >= len(index.ColFields) { - var keyParams string - for i, key := range keys { - keyParams += key - if i != len(keys)-1 { - keyParams += ", " - } - } - keyType := fmt.Sprintf("Index_%sKey", index.Name()) - indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P(helper.Indent(depth+1), keyType, " key{", keyParams, "};") - g.P(helper.Indent(depth+1), indexContainerName, "[key].push_back(&", parentDataName, ");") - return keys - } - field := index.ColFields[cursor] - if field.FD.IsList() { - itemName := fmt.Sprintf("index_item%d", cursor) - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" - } - g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") - key := itemName - if field.FD.Enum() != nil { - key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" - } - keys = append(keys, key) - keys = generateOneCppMulticolumnIndex(g, depth+1, index, parentDataName, keys) - g.P(helper.Indent(depth+1), "}") - } else { - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" - } - key := parentDataName + fieldName - keys = append(keys, key) - keys = generateOneCppMulticolumnIndex(g, depth, index, parentDataName, keys) - } - return keys -} - -func genCppIndexFinders(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - vectorType := "Index_" + index.Name() + "Vector" - mapType := "Index_" + index.Name() + "Map" - indexContainerName := "index_" + strcase.ToSnake(index.Name()) + "_map_" - - g.P("// Index: ", index.Index) - g.P("const ", messagerName, "::", mapType, "& ", messagerName, "::Find", index.Name(), "Map() const { return ", indexContainerName, " ;}") - g.P() - - var keys []helper.MapKey - for _, field := range index.ColFields { - keys = append(keys, helper.MapKey{ - Type: helper.ParseCppType(field.FD), - Name: helper.ParseIndexFieldNameAsFuncParam(field.FD), - }) - } - - g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", helper.GenGetParams(keys), ") const {") - if len(index.ColFields) == 1 { - g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", helper.GenGetArguments(keys), ");") - } else { - g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find({", helper.GenGetArguments(keys), "});") - } - g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") - g.P(helper.Indent(2), "return nullptr;") - g.P(helper.Indent(1), "}") - g.P(helper.Indent(1), "return &iter->second;") - g.P("}") - g.P() - - g.P("const ", helper.ParseCppClassType(index.MD), "* ", messagerName, "::FindFirst", index.Name(), "(", helper.GenGetParams(keys), ") const {") - g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", helper.GenGetArguments(keys), ");") - g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") - g.P(helper.Indent(2), "return nullptr;") - g.P(helper.Indent(1), "}") - g.P(helper.Indent(1), "return conf->front();") - g.P("}") - g.P() - } - } -} diff --git a/cmd/protoc-gen-cpp-tableau-loader/index/index.go b/cmd/protoc-gen-cpp-tableau-loader/index/index.go new file mode 100644 index 00000000..14f4562a --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/index/index.go @@ -0,0 +1,309 @@ +package index + +import ( + "fmt" + "strings" + + "github.com/iancoleman/strcase" + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/options" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type Generator struct { + g *protogen.GeneratedFile + descriptor *index.IndexDescriptor + message *protogen.Message +} + +func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { + return &Generator{ + g: g, + descriptor: descriptor, + message: message, + } +} + +func (x *Generator) NeedGenerate() bool { + return options.NeedGenIndex(x.message.Desc, options.LangCPP) +} + +func (x *Generator) messagerName() string { + return string(x.message.Desc.Name()) +} + +func (x *Generator) mapType(index *index.LevelIndex) string { + return fmt.Sprintf("Index_%sMap", index.Name()) +} + +func (x *Generator) mapKeyType(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + return helper.ParseCppType(field.FD) + } else { + // multi-column index + return fmt.Sprintf("Index_%sKey", index.Name()) + } +} + +func (x *Generator) mapValueType(index *index.LevelIndex) string { + return helper.ParseCppClassType(index.MD) +} + +func (x *Generator) mapValueVectorType(index *index.LevelIndex) string { + return fmt.Sprintf("Index_%sVector", index.Name()) +} + +func (x *Generator) indexContainerName(index *index.LevelIndex) string { + return fmt.Sprintf("index_%s_map_", strcase.ToSnake(index.Name())) +} + +func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeys { + var keys helper.MapKeys + for _, field := range index.ColFields { + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseCppType(field.FD), + Name: helper.ParseIndexFieldNameAsFuncParam(field.FD), + }) + } + return keys +} + +func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { + return fmt.Sprintf(".%s()", helper.ParseIndexFieldName(fd)) +} + +func (x *Generator) parseKeyFieldName(field *index.LevelField) string { + var fieldName string + for _, leveledFd := range field.LeveledFDList { + fieldName += x.fieldGetter(leveledFd) + } + return fieldName +} + +func (x *Generator) GenHppIndexFinders() { + if !x.NeedGenerate() { + return + } + x.g.P() + x.g.P(helper.Indent(1), "// Index accessers.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P(helper.Indent(1), "// Index: ", index.Index) + x.g.P(" public:") + mapType := x.mapType(index) + keyType := x.mapKeyType(index) + vectorType := x.mapValueVectorType(index) + valueType := x.mapValueType(index) + keys := x.indexKeys(index) + hasher := "" // std::hash by default + if len(index.ColFields) != 1 { + // multi-column index + keyHasherType := fmt.Sprintf("Index_%sKeyHasher", index.Name()) + hasher = ", " + keyHasherType + // Generate key struct + x.g.P(helper.Indent(1), "struct ", keyType, " {") + for _, key := range keys { + x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") + } + x.g.P("#if __cplusplus >= 202002L") + x.g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const = default;") + x.g.P("#else") + x.g.P(helper.Indent(2), "bool operator==(const ", keyType, "& other) const {") + if len(keys) == 1 { + x.g.P(helper.Indent(3), "return ", keys[0].Name, " == other.", keys[0].Name, ";") + } else { + x.g.P(helper.Indent(3), "return std::tie(", keys.GenGetArguments(), ") == std::tie(", keys.GenOtherArguments("other"), ");") + } + // x.g.P(helper.Indent(3), "return ", strings.Join(equalities, " && "), ";") + x.g.P(helper.Indent(2), "}") + x.g.P("#endif") + x.g.P(helper.Indent(1), "};") + + // Generate key hasher struct + x.g.P(helper.Indent(1), "struct ", keyHasherType, " {") + x.g.P(helper.Indent(2), "std::size_t operator()(const ", keyType, "& key) const {") + x.g.P(helper.Indent(3), "return util::SugaredHashCombine(", keys.GenOtherArguments("key"), ");") + x.g.P(helper.Indent(2), "}") + x.g.P(helper.Indent(1), "};") + } + x.g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") + x.g.P(helper.Indent(1), "using ", mapType, " = std::unordered_map<", keyType, ", ", vectorType, hasher, ">;") + x.g.P(helper.Indent(1), "// Finds the index (", index.Index, ") to value (", vectorType, ") hash map.") + x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are contained by a vector.") + x.g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "() const;") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s).") + 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;") + x.g.P() + x.g.P(" private:") + x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index), ";") + x.g.P() + } + } +} + +func (x *Generator) GenCppIndexLoader() { + if !x.NeedGenerate() { + return + } + x.g.P(helper.Indent(1), "// Index init.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P(helper.Indent(1), x.indexContainerName(index), ".clear();") + } + } + parentDataName := "data_" + depth := 1 + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.genOneCppIndexLoader(depth, index, parentDataName) + } + itemName := fmt.Sprintf("item%d", depth) + if levelMessage.FD == nil { + break + } + if !levelMessage.NextLevel.NeedGen() { + break + } + x.g.P(helper.Indent(depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") + parentDataName = itemName + if levelMessage.FD.IsMap() { + parentDataName = itemName + ".second" + } + depth++ + } + for i := depth - 1; i > 0; i-- { + x.g.P(helper.Indent(i), "}") + } + x.genIndexSorter() +} + +func (x *Generator) genOneCppIndexLoader(depth int, index *index.LevelIndex, parentDataName string) { + x.g.P(helper.Indent(depth), "{") + x.g.P(helper.Indent(depth+1), "// Index: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + fieldName := x.parseKeyFieldName(field) + indexContainerName := x.indexContainerName(index) + if field.FD.IsList() { + itemName := fmt.Sprintf("item%d", depth) + x.g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") + key := itemName + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + x.g.P(helper.Indent(depth+2), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") + x.g.P(helper.Indent(depth+1), "}") + } else { + key := parentDataName + fieldName + x.g.P(helper.Indent(depth+1), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") + } + } else { + // multi-column index + x.generateOneCppMulticolumnIndex(depth, index, parentDataName, nil) + } + x.g.P(helper.Indent(depth), "}") +} + +func (x *Generator) generateOneCppMulticolumnIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeys) { + cursor := len(keys) + if cursor >= len(index.ColFields) { + keyType := x.mapKeyType(index) + indexContainerName := x.indexContainerName(index) + x.g.P(helper.Indent(depth+1), keyType, " key{", keys.GenGetArguments(), "};") + x.g.P(helper.Indent(depth+1), indexContainerName, "[key].push_back(&", parentDataName, ");") + return + } + field := index.ColFields[cursor] + fieldName := x.parseKeyFieldName(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("index_item%d", cursor) + x.g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") + key := itemName + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneCppMulticolumnIndex(depth+1, index, parentDataName, keys) + x.g.P(helper.Indent(depth+1), "}") + } else { + key := parentDataName + fieldName + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneCppMulticolumnIndex(depth, index, parentDataName, keys) + } +} + +func (x *Generator) genIndexSorter() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + if len(index.SortedColFields) != 0 { + valueType := x.mapValueType(index) + x.g.P(helper.Indent(1), "// Index(sort): ", index.Index) + x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index), ") {") + x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(),") + x.g.P(helper.Indent(7), "[](const ", valueType, "* a, const ", valueType, "* b) {") + for i, field := range index.SortedColFields { + fieldName := strings.Replace(x.parseKeyFieldName(field), ".", "->", 1) + if i == len(index.SortedColFields)-1 { + x.g.P(helper.Indent(8), "return a", fieldName, " < b", fieldName, ";") + } else { + x.g.P(helper.Indent(8), "if (a", fieldName, " != b", fieldName, ") {") + x.g.P(helper.Indent(9), "return a", fieldName, " < b", fieldName, ";") + x.g.P(helper.Indent(8), "}") + } + } + x.g.P(helper.Indent(7), "});") + x.g.P(helper.Indent(1), "}") + } + } + } +} + +func (x *Generator) GenCppIndexFinders() { + if !x.NeedGenerate() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + vectorType := x.mapValueVectorType(index) + mapType := x.mapType(index) + indexContainerName := x.indexContainerName(index) + messagerName := x.messagerName() + + x.g.P("// Index: ", index.Index) + x.g.P("const ", messagerName, "::", mapType, "& ", messagerName, "::Find", index.Name(), "() const { return ", indexContainerName, " ;}") + x.g.P() + + keys := x.indexKeys(index) + params := keys.GenGetParams() + args := keys.GenGetArguments() + x.g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", params, ") const {") + if len(index.ColFields) == 1 { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", args, ");") + } else { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find({", args, "});") + } + x.g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second;") + x.g.P("}") + x.g.P() + + x.g.P("const ", x.mapValueType(index), "* ", messagerName, "::FindFirst", index.Name(), "(", params, ") const {") + x.g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", args, ");") + x.g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return conf->front();") + x.g.P("}") + x.g.P() + } + } +} diff --git a/cmd/protoc-gen-cpp-tableau-loader/messager.go b/cmd/protoc-gen-cpp-tableau-loader/messager.go index 7228a4fa..a9f6f0a3 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/messager.go +++ b/cmd/protoc-gen-cpp-tableau-loader/messager.go @@ -4,9 +4,11 @@ import ( "strings" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + idx "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/index" + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/orderedindex" + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/orderedmap" "github.com/tableauio/loader/internal/extensions" "github.com/tableauio/loader/internal/index" - "github.com/tableauio/loader/internal/options" "github.com/tableauio/tableau/proto/tableaupb" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" @@ -80,9 +82,12 @@ func generateHppFileContent(file *protogen.File, g *protogen.GeneratedFile) { // genHppMessage generates a message definition. func genHppMessage(g *protogen.GeneratedFile, message *protogen.Message) { cppFullName := helper.ParseCppClassType(message.Desc) - messagerFullName := string(message.Desc.FullName()) indexDescriptor := index.ParseIndexDescriptor(message.Desc) + orderedMapGenerator := orderedmap.NewGenerator(g, message) + indexGenerator := idx.NewGenerator(g, indexDescriptor, message) + orderedIndexGenerator := orderedindex.NewGenerator(g, indexDescriptor, message) + g.P("class ", message.Desc.Name(), " : public Messager {") g.P(" public:") g.P(helper.Indent(1), "static const std::string& Name() { return kProtoName; }") @@ -91,7 +96,7 @@ func genHppMessage(g *protogen.GeneratedFile, message *protogen.Message) { g.P(helper.Indent(1), "const google::protobuf::Message* Message() const override { return &data_; }") g.P() - if options.NeedGenOrderedMap(message.Desc, options.LangCPP) || options.NeedGenIndex(message.Desc, options.LangCPP) || options.NeedGenOrderedIndex(message.Desc, options.LangCPP) { + if orderedMapGenerator.NeedGenerate() || indexGenerator.NeedGenerate() || orderedIndexGenerator.NeedGenerate() { g.P(" private:") g.P(helper.Indent(1), "virtual bool ProcessAfterLoad() override final;") g.P() @@ -103,30 +108,25 @@ func genHppMessage(g *protogen.GeneratedFile, message *protogen.Message) { g.P(" private:") g.P(helper.Indent(1), "static const std::string kProtoName;") g.P(helper.Indent(1), cppFullName, " data_;") - if options.NeedGenOrderedMap(message.Desc, options.LangCPP) { - g.P() - genHppOrderedMapGetters(g, message.Desc, 1, nil, messagerFullName) - } - if options.NeedGenIndex(message.Desc, options.LangCPP) { - g.P() - genHppIndexFinders(g, indexDescriptor) - } - if options.NeedGenOrderedIndex(message.Desc, options.LangCPP) { - genHppOrderedIndexFinders(g, indexDescriptor) - } + orderedMapGenerator.GenHppOrderedMapGetters() + indexGenerator.GenHppIndexFinders() + orderedIndexGenerator.GenHppOrderedIndexFinders() g.P("};") g.P() } -func genHppMapGetters(depth int, keys []helper.MapKey, g *protogen.GeneratedFile, md protoreflect.MessageDescriptor) { +func genHppMapGetters(depth int, keys helper.MapKeys, g *protogen.GeneratedFile, md protoreflect.MessageDescriptor) { for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) if fd.IsMap() { if depth == 1 { g.P(" public:") } - keys = helper.AddMapKey(fd, keys) - g.P(helper.Indent(1), "const ", helper.ParseCppType(fd.MapValue()), "* Get(", helper.GenGetParams(keys), ") const;") + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) + g.P(helper.Indent(1), "const ", helper.ParseCppType(fd.MapValue()), "* Get(", keys.GenGetParams(), ") const;") if fd.MapValue().Kind() == protoreflect.MessageKind { genHppMapGetters(depth+1, keys, g, fd.MapValue().Message()) } @@ -157,10 +157,13 @@ func generateCppFileContent(file *protogen.File, g *protogen.GeneratedFile) { // genCppMessage generates a message implementation. func genCppMessage(g *protogen.GeneratedFile, message *protogen.Message) { messagerName := string(message.Desc.Name()) - messagerFullName := string(message.Desc.FullName()) cppFullName := helper.ParseCppClassType(message.Desc) indexDescriptor := index.ParseIndexDescriptor(message.Desc) + orderedMapGenerator := orderedmap.NewGenerator(g, message) + indexGenerator := idx.NewGenerator(g, indexDescriptor, message) + orderedIndexGenerator := orderedindex.NewGenerator(g, indexDescriptor, message) + g.P("const std::string ", messagerName, "::kProtoName = ", cppFullName, `::GetDescriptor()->name();`) g.P() g.P("bool ", messagerName, "::Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options /* = nullptr */) {") @@ -172,17 +175,11 @@ func genCppMessage(g *protogen.GeneratedFile, message *protogen.Message) { g.P("}") g.P() - if options.NeedGenOrderedMap(message.Desc, options.LangCPP) || options.NeedGenIndex(message.Desc, options.LangCPP) || options.NeedGenOrderedIndex(message.Desc, options.LangCPP) { + if orderedMapGenerator.NeedGenerate() || indexGenerator.NeedGenerate() || orderedIndexGenerator.NeedGenerate() { g.P("bool ", messagerName, "::ProcessAfterLoad() {") - if options.NeedGenOrderedMap(message.Desc, options.LangCPP) { - genCppOrderedMapLoader(g, message.Desc, 1, messagerFullName) - } - if options.NeedGenIndex(message.Desc, options.LangCPP) { - genCppIndexLoader(g, indexDescriptor) - } - if options.NeedGenOrderedIndex(message.Desc, options.LangCPP) { - genCppOrderedIndexLoader(g, indexDescriptor) - } + orderedMapGenerator.GenOrderedMapLoader() + indexGenerator.GenCppIndexLoader() + orderedIndexGenerator.GenCppOrderedIndexLoader() g.P(helper.Indent(1), "return true;") g.P("}") g.P() @@ -190,23 +187,20 @@ func genCppMessage(g *protogen.GeneratedFile, message *protogen.Message) { // syntactic sugar for accessing map items genCppMapGetters(g, message.Desc, 1, nil, messagerName) - genCppOrderedMapGetters(g, message.Desc, 1, nil, messagerName, messagerFullName) - if options.NeedGenIndex(message.Desc, options.LangCPP) { - genCppIndexFinders(g, indexDescriptor, messagerName) - g.P() - } - if options.NeedGenOrderedIndex(message.Desc, options.LangCPP) { - genCppOrderedIndexFinders(g, indexDescriptor, messagerName) - g.P() - } + orderedMapGenerator.GenOrderedMapGetters() + indexGenerator.GenCppIndexFinders() + orderedIndexGenerator.GenCppOrderedIndexFinders() } -func genCppMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys []helper.MapKey, messagerName string) { +func genCppMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys, messagerName string) { for i := 0; i < md.Fields().Len(); i++ { fd := md.Fields().Get(i) if fd.IsMap() { - keys = helper.AddMapKey(fd, keys) - g.P("const ", helper.ParseCppType(fd.MapValue()), "* ", messagerName, "::Get(", helper.GenGetParams(keys), ") const {") + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) + g.P("const ", helper.ParseCppType(fd.MapValue()), "* ", messagerName, "::Get(", keys.GenGetParams(), ") const {") var container string if depth == 1 { @@ -214,7 +208,7 @@ func genCppMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageDescript } else { container = "conf->" + string(fd.Name()) + "()" prevKeys := keys[:len(keys)-1] - g.P(helper.Indent(1), "const auto* conf = Get(", helper.GenGetArguments(prevKeys), ");") + g.P(helper.Indent(1), "const auto* conf = Get(", prevKeys.GenGetArguments(), ");") g.P(helper.Indent(1), "if (conf == nullptr) {") g.P(helper.Indent(2), "return nullptr;") g.P(helper.Indent(1), "}") @@ -235,24 +229,3 @@ func genCppMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageDescript } } } - -func getNextLevelMapFD(fd protoreflect.FieldDescriptor) protoreflect.FieldDescriptor { - if fd.Kind() == protoreflect.MessageKind { - md := fd.Message() - for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() { - return fd - } - } - } - return nil -} - -func parseMapValueType(fd protoreflect.FieldDescriptor) string { - valueType := helper.ParseCppType(fd.MapValue()) - if fd.MapValue().Kind() == protoreflect.MessageKind { - return "const " + valueType + "*" - } - return valueType -} diff --git a/cmd/protoc-gen-cpp-tableau-loader/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/ordered_index.go deleted file mode 100644 index 6b675fc8..00000000 --- a/cmd/protoc-gen-cpp-tableau-loader/ordered_index.go +++ /dev/null @@ -1,190 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" - "google.golang.org/protobuf/compiler/protogen" -) - -func genHppOrderedIndexFinders(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - g.P(helper.Indent(1), "// OrderedIndex accessers.") - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - // single-column index - field := index.ColFields[0] // just take first field - g.P(helper.Indent(1), "// OrderedIndex: ", index.Index) - g.P(" public:") - vectorType := fmt.Sprintf("OrderedIndex_%sVector", index.Name()) - mapType := fmt.Sprintf("OrderedIndex_%sMap", index.Name()) - g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") - keyType := helper.ParseOrderedMapKeyType(field.FD) - g.P(helper.Indent(1), "using ", mapType, " = std::map<", keyType, ", ", vectorType, ">;") - g.P(helper.Indent(1), "// Finds the ordered index (", index.Index, ") to value (", vectorType, ") hash map.") - g.P(helper.Indent(1), "// One key may correspond to multiple values, which are contained by a vector.") - g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "() const;") - g.P(helper.Indent(1), "// Finds a vector of all values of the given key.") - g.P(helper.Indent(1), "const ", vectorType, "* Find", index.Name(), "(", helper.ToConstRefType(keyType), " ", helper.ParseIndexFieldNameAsFuncParam(field.FD), ") const;") - g.P(helper.Indent(1), "// Finds the first value of the given key.") - g.P(helper.Indent(1), "const ", helper.ParseCppClassType(index.MD), "* FindFirst", index.Name(), "(", helper.ToConstRefType(keyType), " ", helper.ParseIndexFieldNameAsFuncParam(field.FD), ") const;") - g.P() - - g.P(" private:") - indexContainerName := "ordered_index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P(helper.Indent(1), mapType, " ", indexContainerName, ";") - g.P() - } - } -} - -func genCppOrderedIndexLoader(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - g.P(helper.Indent(1), "// OrderedIndex init.") - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - indexContainerName := "ordered_index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P(helper.Indent(1), indexContainerName, ".clear();") - } - } - parentDataName := "data_" - depth := 1 - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - genOneCppOrderedIndexLoader(g, depth, index, parentDataName) - } - itemName := fmt.Sprintf("item%d", depth) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGen() { - break - } - g.P(helper.Indent(depth), "for (auto&& ", itemName, " : ", parentDataName, ".", helper.ParseIndexFieldName(levelMessage.FD), "()) {") - parentDataName = itemName - if levelMessage.FD.IsMap() { - parentDataName = itemName + ".second" - } - depth++ - } - for i := depth - 1; i > 0; i-- { - g.P(helper.Indent(i), "}") - } - genOrderedIndexSorter(g, descriptor) -} - -func genOneCppOrderedIndexLoader(g *protogen.GeneratedFile, depth int, index *index.LevelIndex, parentDataName string) { - indexContainerName := "ordered_index_" + strcase.ToSnake(index.Name()) + "_map_" - g.P(helper.Indent(depth), "{") - g.P(helper.Indent(depth+1), "// OrderedIndex: ", index.Index) - // single-column index - field := index.ColFields[0] // just take the first field - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - fieldName := "" - suffix := "" - for i, leveledFd := range field.LeveledFDList { - fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" - if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { - switch leveledFd.Message().FullName() { - case "google.protobuf.Timestamp", "google.protobuf.Duration": - suffix = ".seconds()" - default: - } - } - } - g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") - key := itemName + suffix - if field.FD.Enum() != nil { - key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" - } - g.P(helper.Indent(depth+2), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") - g.P(helper.Indent(depth+1), "}") - } else { - fieldName := "" - suffix := "" - for i, leveledFd := range field.LeveledFDList { - fieldName += "." + helper.ParseIndexFieldName(leveledFd) + "()" - if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { - switch leveledFd.Message().FullName() { - case "google.protobuf.Timestamp", "google.protobuf.Duration": - suffix = ".seconds()" - default: - } - } - } - key := parentDataName + fieldName + suffix - g.P(helper.Indent(depth+1), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") - } - g.P(helper.Indent(depth), "}") -} - -func genOrderedIndexSorter(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - indexContainerName := "ordered_index_" + strcase.ToSnake(index.Name()) + "_map_" - if len(index.SortedColFields) != 0 { - g.P(helper.Indent(1), "// OrderedIndex(sort): ", index.Index) - g.P(helper.Indent(1), "for (auto&& item : ", indexContainerName, ") {") - g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(),") - g.P(helper.Indent(7), "[](const ", helper.ParseCppClassType(index.MD), "* a, const ", helper.ParseCppClassType(index.MD), "* b) {") - for i, field := range index.SortedColFields { - fieldName := "" - for i, leveledFd := range field.LeveledFDList { - accessOperator := "." - if i == 0 { - accessOperator = "->" - } - fieldName += accessOperator + helper.ParseIndexFieldName(leveledFd) + "()" - } - if i == len(index.SortedColFields)-1 { - g.P(helper.Indent(8), "return a", fieldName, " < b", fieldName, ";") - } else { - g.P(helper.Indent(8), "if (a", fieldName, " != b", fieldName, ") {") - g.P(helper.Indent(9), "return a", fieldName, " < b", fieldName, ";") - g.P(helper.Indent(8), "}") - } - } - g.P(helper.Indent(7), "});") - g.P(helper.Indent(1), "}") - } - } - } -} - -func genCppOrderedIndexFinders(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - vectorType := "OrderedIndex_" + index.Name() + "Vector" - mapType := "OrderedIndex_" + index.Name() + "Map" - indexContainerName := "ordered_index_" + strcase.ToSnake(index.Name()) + "_map_" - - g.P("// OrderedIndex: ", index.Index) - g.P("const ", messagerName, "::", mapType, "& ", messagerName, "::Find", index.Name(), "() const { return ", indexContainerName, " ;}") - g.P() - - // single-column index - field := index.ColFields[0] // just take first field - keyType := helper.ParseOrderedMapKeyType(field.FD) - keyName := helper.ParseIndexFieldNameAsFuncParam(field.FD) - - g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", helper.ToConstRefType(keyType), " ", keyName, ") const {") - g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", keyName, ");") - g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") - g.P(helper.Indent(2), "return nullptr;") - g.P(helper.Indent(1), "}") - g.P(helper.Indent(1), "return &iter->second;") - g.P("}") - g.P() - - g.P("const ", helper.ParseCppClassType(index.MD), "* ", messagerName, "::FindFirst", index.Name(), "(", helper.ToConstRefType(keyType), " ", keyName, ") const {") - g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", keyName, ");") - g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") - g.P(helper.Indent(2), "return nullptr;") - g.P(helper.Indent(1), "}") - g.P(helper.Indent(1), "return conf->front();") - g.P("}") - g.P() - } - } -} diff --git a/cmd/protoc-gen-cpp-tableau-loader/ordered_map.go b/cmd/protoc-gen-cpp-tableau-loader/ordered_map.go deleted file mode 100644 index 8c560e90..00000000 --- a/cmd/protoc-gen-cpp-tableau-loader/ordered_map.go +++ /dev/null @@ -1,153 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/options" - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/reflect/protoreflect" -) - -const orderedMapSuffix = "_OrderedMap" -const orderedMapValueSuffix = "_OrderedMapValue" - -func genHppOrderedMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys []helper.MapKey, messagerFullName string) { - if depth == 1 && !options.NeedGenOrderedMap(md, options.LangCPP) { - return - } - for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() { - if depth == 1 { - g.P(helper.Indent(1), "// OrderedMap accessers.") - g.P(" public:") - } - nextKeys := helper.AddMapKey(fd, keys) - keyType := nextKeys[len(nextKeys)-1].Type - - if fd.MapValue().Kind() == protoreflect.MessageKind { - genHppOrderedMapGetters(g, fd.MapValue().Message(), depth+1, nextKeys, messagerFullName) - } - - prefix := parseOrderedMapPrefix(fd, messagerFullName) - orderedMap := prefix + orderedMapSuffix - orderedMapValue := prefix + orderedMapValueSuffix - - nextMapFD := getNextLevelMapFD(fd.MapValue()) - if nextMapFD != nil { - currValueType := helper.ParseCppType(fd.MapValue()) - nextPrefix := parseOrderedMapPrefix(nextMapFD, messagerFullName) - nextOrderedMap := nextPrefix + orderedMapSuffix - // nextOrderedMapValue := nextPrefix + orderedMapValueSuffix - g.P(helper.Indent(1), "using ", orderedMapValue, " = std::pair<", nextOrderedMap, ", const ", currValueType, "*>;") - g.P(helper.Indent(1), "using ", orderedMap, " = std::map<", keyType, ", ", orderedMapValue, ">;") - g.P(helper.Indent(1), "const ", orderedMap, "* GetOrderedMap(", helper.GenGetParams(keys), ") const;") - g.P() - } else { - g.P(helper.Indent(1), "using ", orderedMap, " = std::map<", keyType, ", ", parseMapValueType(fd), ">;") - g.P(helper.Indent(1), "const ", orderedMap, "* GetOrderedMap(", helper.GenGetParams(keys), ") const;") - g.P() - } - if depth == 1 { - g.P(" private:") - g.P(helper.Indent(1), orderedMap, " ordered_map_;") - } - break - } - } -} - -func genCppOrderedMapLoader(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, messagerFullName string) { - if depth == 1 { - g.P(helper.Indent(1), "// OrderedMap init.") - g.P(helper.Indent(1), "ordered_map_.clear();") - } - for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() { - prefix := parseOrderedMapPrefix(fd, messagerFullName) - // orderedMap := prefix + orderedMapSuffix - orderedMapValue := prefix + orderedMapValueSuffix - itemName := fmt.Sprintf("item%d", depth) - - tmpOrderedMapName := fmt.Sprintf("ordered_map%d", depth) - - prevItemName := fmt.Sprintf("item%d", depth-1) - prevContainer := prevItemName + ".second" - prevTmpOrderedMapName := fmt.Sprintf("ordered_map%d", depth-1) - if depth == 1 { - prevContainer = "data_" - prevTmpOrderedMapName = "ordered_map_" - } - g.P(helper.Indent(depth), "for (auto&& ", itemName, " : ", prevContainer, ".", string(fd.Name()), "()) {") - nextMapFD := getNextLevelMapFD(fd.MapValue()) - if nextMapFD != nil { - nextPrefix := parseOrderedMapPrefix(nextMapFD, messagerFullName) - // nextMap := nextPrefix + mapSuffix - nextOrderedMap := nextPrefix + orderedMapSuffix - g.P(helper.Indent(depth+1), prevTmpOrderedMapName, "[", itemName, ".first] = ", orderedMapValue, "(", nextOrderedMap, "(), &", itemName, ".second);") - g.P(helper.Indent(depth+1), "auto&& ", tmpOrderedMapName, " = ", prevTmpOrderedMapName, "[", itemName, ".first].first;") - } else { - ref := "&" - if fd.MapValue().Kind() != protoreflect.MessageKind { - ref = "" // scalar value type just do value copy. - } - g.P(helper.Indent(depth+1), prevTmpOrderedMapName, "[", itemName, ".first] = ", ref, itemName, ".second;") - } - if fd.MapValue().Kind() == protoreflect.MessageKind { - genCppOrderedMapLoader(g, fd.MapValue().Message(), depth+1, messagerFullName) - } - g.P(helper.Indent(depth), "}") - break - } - } -} - -func genCppOrderedMapGetters(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys []helper.MapKey, messagerName, messagerFullName string) { - if depth == 1 && !options.NeedGenOrderedMap(md, options.LangCPP) { - return - } - for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() { - prefix := parseOrderedMapPrefix(fd, messagerFullName) - orderedMap := prefix + orderedMapSuffix - - g.P("const ", messagerName, "::", orderedMap, "* ", messagerName, "::GetOrderedMap(", helper.GenGetParams(keys), ") const {") - if depth == 1 { - g.P(helper.Indent(1), "return &ordered_map_; ") - } else { - lastKeyName := keys[len(keys)-1].Name - prevKeys := keys[:len(keys)-1] - g.P(helper.Indent(1), "const auto* conf = GetOrderedMap(", helper.GenGetArguments(prevKeys), ");") - g.P(helper.Indent(1), "if (conf == nullptr) {") - g.P(helper.Indent(2), "return nullptr;") - g.P(helper.Indent(1), "}") - g.P(helper.Indent(1), "auto iter = conf->find(", lastKeyName, ");") - g.P(helper.Indent(1), "if (iter == conf->end()) {") - g.P(helper.Indent(2), "return nullptr;") - g.P(helper.Indent(1), "}") - g.P(helper.Indent(1), "return &iter->second.first;") - - } - g.P("}") - g.P() - - keys = helper.AddMapKey(fd, keys) - if fd.MapValue().Kind() == protoreflect.MessageKind { - genCppOrderedMapGetters(g, fd.MapValue().Message(), depth+1, keys, messagerName, messagerFullName) - } - break - } - } -} - -func parseOrderedMapPrefix(mapFd protoreflect.FieldDescriptor, messagerFullName string) string { - if mapFd.MapValue().Kind() == protoreflect.MessageKind { - localMsgProtoName := strings.TrimPrefix(string(mapFd.MapValue().Message().FullName()), messagerFullName+".") - return strings.ReplaceAll(localMsgProtoName, ".", "_") - } - return mapFd.MapValue().Kind().String() -} diff --git a/cmd/protoc-gen-cpp-tableau-loader/orderedindex/ordered_index.go b/cmd/protoc-gen-cpp-tableau-loader/orderedindex/ordered_index.go new file mode 100644 index 00000000..7650a389 --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/orderedindex/ordered_index.go @@ -0,0 +1,306 @@ +package orderedindex + +import ( + "fmt" + "strings" + + "github.com/iancoleman/strcase" + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/options" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type Generator struct { + g *protogen.GeneratedFile + descriptor *index.IndexDescriptor + message *protogen.Message +} + +func NewGenerator(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { + return &Generator{ + g: g, + descriptor: descriptor, + message: message, + } +} + +func (x *Generator) NeedGenerate() bool { + return options.NeedGenOrderedIndex(x.message.Desc, options.LangCPP) +} + +func (x *Generator) messagerName() string { + return string(x.message.Desc.Name()) +} + +func (x *Generator) mapType(index *index.LevelIndex) string { + return fmt.Sprintf("OrderedIndex_%sMap", index.Name()) +} + +func (x *Generator) mapKeyType(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + return helper.ParseOrderedIndexKeyType(field.FD) + } else { + // multi-column index + return fmt.Sprintf("OrderedIndex_%sKey", index.Name()) + } +} + +func (x *Generator) mapValueType(index *index.LevelIndex) string { + return helper.ParseCppClassType(index.MD) +} + +func (x *Generator) mapValueVectorType(index *index.LevelIndex) string { + return fmt.Sprintf("OrderedIndex_%sVector", index.Name()) +} + +func (x *Generator) indexContainerName(index *index.LevelIndex) string { + return fmt.Sprintf("ordered_index_%s_map_", strcase.ToSnake(index.Name())) +} + +func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeys { + var keys helper.MapKeys + for _, field := range index.ColFields { + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseOrderedIndexKeyType(field.FD), + Name: helper.ParseIndexFieldNameAsFuncParam(field.FD), + }) + } + return keys +} + +func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { + return fmt.Sprintf(".%s()", helper.ParseIndexFieldName(fd)) +} + +func (x *Generator) parseKeyFieldNameAndSuffix(field *index.LevelField) (string, string) { + var fieldName, suffix string + for i, leveledFd := range field.LeveledFDList { + fieldName += x.fieldGetter(leveledFd) + if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { + switch leveledFd.Message().FullName() { + case "google.protobuf.Timestamp", "google.protobuf.Duration": + suffix = ".seconds()" + default: + } + } + } + return fieldName, suffix +} + +func (x *Generator) GenHppOrderedIndexFinders() { + if !x.NeedGenerate() { + return + } + x.g.P(helper.Indent(1), "// OrderedIndex accessers.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P(helper.Indent(1), "// OrderedIndex: ", index.Index) + x.g.P(" public:") + mapType := x.mapType(index) + keyType := x.mapKeyType(index) + vectorType := x.mapValueVectorType(index) + valueType := x.mapValueType(index) + keys := x.indexKeys(index) + if len(index.ColFields) != 1 { + // multi-column index + // Generate key struct + x.g.P(helper.Indent(1), "struct ", keyType, " {") + for _, key := range keys { + x.g.P(helper.Indent(2), key.Type, " ", key.Name, ";") + } + x.g.P("#if __cplusplus >= 202002L") + x.g.P(helper.Indent(2), "auto operator<=>(const ", keyType, "& other) const = default;") + x.g.P("#else") + x.g.P(helper.Indent(2), "bool operator<(const ", keyType, "& other) const {") + if len(keys) == 1 { + x.g.P(helper.Indent(3), "return ", keys[0].Name, " < other.", keys[0].Name, ";") + } else { + x.g.P(helper.Indent(3), "return std::tie(", keys.GenGetArguments(), ") < std::tie(", keys.GenOtherArguments("other"), ");") + } + x.g.P(helper.Indent(2), "}") + x.g.P("#endif") + x.g.P(helper.Indent(1), "};") + } + x.g.P(helper.Indent(1), "using ", vectorType, " = std::vector;") + x.g.P(helper.Indent(1), "using ", mapType, " = std::map<", keyType, ", ", vectorType, ">;") + x.g.P(helper.Indent(1), "// Finds the ordered index (", index.Index, ") to value (", vectorType, ") map.") + x.g.P(helper.Indent(1), "// One key may correspond to multiple values, which are contained by a vector.") + x.g.P(helper.Indent(1), "const ", mapType, "& Find", index.Name(), "() const;") + x.g.P(helper.Indent(1), "// Finds a vector of all values of the given key(s).") + 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;") + x.g.P() + + x.g.P(" private:") + x.g.P(helper.Indent(1), mapType, " ", x.indexContainerName(index), ";") + x.g.P() + } + } +} + +func (x *Generator) GenCppOrderedIndexLoader() { + if !x.NeedGenerate() { + return + } + x.g.P(helper.Indent(1), "// OrderedIndex init.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P(helper.Indent(1), x.indexContainerName(index), ".clear();") + } + } + parentDataName := "data_" + depth := 1 + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.genOneCppOrderedIndexLoader(depth, index, parentDataName) + } + itemName := fmt.Sprintf("item%d", depth) + if levelMessage.FD == nil { + break + } + if !levelMessage.NextLevel.NeedGen() { + break + } + x.g.P(helper.Indent(depth), "for (auto&& ", itemName, " : ", parentDataName, x.fieldGetter(levelMessage.FD), ") {") + parentDataName = itemName + if levelMessage.FD.IsMap() { + parentDataName = itemName + ".second" + } + depth++ + } + for i := depth - 1; i > 0; i-- { + x.g.P(helper.Indent(i), "}") + } + x.genOrderedIndexSorter() +} + +func (x *Generator) genOneCppOrderedIndexLoader(depth int, index *index.LevelIndex, parentDataName string) { + x.g.P(helper.Indent(depth), "{") + x.g.P(helper.Indent(depth+1), "// OrderedIndex: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) + indexContainerName := x.indexContainerName(index) + if field.FD.IsList() { + itemName := fmt.Sprintf("item%d", depth) + x.g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") + key := itemName + suffix + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + x.g.P(helper.Indent(depth+2), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") + x.g.P(helper.Indent(depth+1), "}") + } else { + key := parentDataName + fieldName + suffix + x.g.P(helper.Indent(depth+1), indexContainerName, "[", key, "].push_back(&", parentDataName, ");") + } + } else { + // multi-column index + x.generateOneCppMulticolumnOrderedIndex(depth, index, parentDataName, nil) + } + x.g.P(helper.Indent(depth), "}") +} + +func (x *Generator) generateOneCppMulticolumnOrderedIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeys) { + cursor := len(keys) + if cursor >= len(index.ColFields) { + keyType := x.mapKeyType(index) + indexContainerName := x.indexContainerName(index) + x.g.P(helper.Indent(depth+1), keyType, " key{", keys.GenGetArguments(), "};") + x.g.P(helper.Indent(depth+1), indexContainerName, "[key].push_back(&", parentDataName, ");") + return + } + field := index.ColFields[cursor] + fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("index_item%d", cursor) + x.g.P(helper.Indent(depth+1), "for (auto&& ", itemName, " : ", parentDataName, fieldName, ") {") + key := itemName + suffix + if field.FD.Enum() != nil { + key = "static_cast<" + helper.ParseCppType(field.FD) + ">(" + key + ")" + } + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneCppMulticolumnOrderedIndex(depth+1, index, parentDataName, keys) + x.g.P(helper.Indent(depth+1), "}") + } else { + key := parentDataName + fieldName + suffix + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneCppMulticolumnOrderedIndex(depth, index, parentDataName, keys) + } +} + +func (x *Generator) genOrderedIndexSorter() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + if len(index.SortedColFields) != 0 { + valueType := x.mapValueType(index) + x.g.P(helper.Indent(1), "// OrderedIndex(sort): ", index.Index) + x.g.P(helper.Indent(1), "for (auto&& item : ", x.indexContainerName(index), ") {") + x.g.P(helper.Indent(2), "std::sort(item.second.begin(), item.second.end(),") + x.g.P(helper.Indent(7), "[](const ", valueType, "* a, const ", valueType, "* b) {") + for i, field := range index.SortedColFields { + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + fieldName = strings.Replace(fieldName, ".", "->", 1) + if i == len(index.SortedColFields)-1 { + x.g.P(helper.Indent(8), "return a", fieldName, " < b", fieldName, ";") + } else { + x.g.P(helper.Indent(8), "if (a", fieldName, " != b", fieldName, ") {") + x.g.P(helper.Indent(9), "return a", fieldName, " < b", fieldName, ";") + x.g.P(helper.Indent(8), "}") + } + } + x.g.P(helper.Indent(7), "});") + x.g.P(helper.Indent(1), "}") + } + } + } +} + +func (x *Generator) GenCppOrderedIndexFinders() { + if !x.NeedGenerate() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + vectorType := x.mapValueVectorType(index) + mapType := x.mapType(index) + indexContainerName := x.indexContainerName(index) + messagerName := x.messagerName() + + x.g.P("// OrderedIndex: ", index.Index) + x.g.P("const ", messagerName, "::", mapType, "& ", messagerName, "::Find", index.Name(), "() const { return ", indexContainerName, " ;}") + x.g.P() + + keys := x.indexKeys(index) + params := keys.GenGetParams() + args := keys.GenGetArguments() + x.g.P("const ", messagerName, "::", vectorType, "* ", messagerName, "::Find", index.Name(), "(", params, ") const {") + if len(index.ColFields) == 1 { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find(", args, ");") + } else { + x.g.P(helper.Indent(1), "auto iter = ", indexContainerName, ".find({", args, "});") + } + x.g.P(helper.Indent(1), "if (iter == ", indexContainerName, ".end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second;") + x.g.P("}") + x.g.P() + + x.g.P("const ", x.mapValueType(index), "* ", messagerName, "::FindFirst", index.Name(), "(", params, ") const {") + x.g.P(helper.Indent(1), "auto conf = Find", index.Name(), "(", args, ");") + x.g.P(helper.Indent(1), "if (conf == nullptr || conf->empty()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return conf->front();") + x.g.P("}") + x.g.P() + } + } +} diff --git a/cmd/protoc-gen-cpp-tableau-loader/orderedmap/ordered_map.go b/cmd/protoc-gen-cpp-tableau-loader/orderedmap/ordered_map.go new file mode 100644 index 00000000..ad51515e --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/orderedmap/ordered_map.go @@ -0,0 +1,203 @@ +package orderedmap + +import ( + "fmt" + "strings" + + "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + "github.com/tableauio/loader/internal/options" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type Generator struct { + g *protogen.GeneratedFile + message *protogen.Message +} + +func NewGenerator(g *protogen.GeneratedFile, message *protogen.Message) *Generator { + return &Generator{ + g: g, + message: message, + } +} + +func (x *Generator) NeedGenerate() bool { + return options.NeedGenOrderedMap(x.message.Desc, options.LangCPP) +} + +func (x *Generator) messagerName() string { + return string(x.message.Desc.Name()) +} + +func (x *Generator) orderedMapPrefix(mapFd protoreflect.FieldDescriptor) string { + if mapFd.MapValue().Kind() == protoreflect.MessageKind { + localMsgProtoName := strings.TrimPrefix(string(mapFd.MapValue().Message().FullName()), string(x.message.Desc.FullName())+".") + return strings.ReplaceAll(localMsgProtoName, ".", "_") + } + return mapFd.MapValue().Kind().String() +} + +func (x *Generator) mapType(mapFd protoreflect.FieldDescriptor) string { + return fmt.Sprintf("OrderedMap_%sMap", x.orderedMapPrefix(mapFd)) +} + +func (x *Generator) mapValueType(mapFd protoreflect.FieldDescriptor) string { + return fmt.Sprintf("OrderedMap_%sValue", x.orderedMapPrefix(mapFd)) +} + +func (x *Generator) mapValueFieldType(fd protoreflect.FieldDescriptor) string { + nextMapFD := getNextLevelMapFD(fd.MapValue()) + if nextMapFD != nil { + return x.mapValueType(fd) + } + return helper.ParseMapValueType(fd) +} + +func (x *Generator) GenHppOrderedMapGetters() { + if !x.NeedGenerate() { + return + } + x.g.P() + x.g.P(helper.Indent(1), "// OrderedMap accessers.") + x.g.P(" public:") + x.genHppOrderedMapGetters(x.message.Desc, 1, nil) +} + +func (x *Generator) genHppOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys) { + for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.IsMap() { + nextKeys := keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) + keyType := nextKeys[len(nextKeys)-1].Type + if fd.MapValue().Kind() == protoreflect.MessageKind { + x.genHppOrderedMapGetters(fd.MapValue().Message(), depth+1, nextKeys) + } + orderedMap := x.mapType(fd) + nextMapFD := getNextLevelMapFD(fd.MapValue()) + if nextMapFD != nil { + orderedMapValue := x.mapValueType(fd) + currValueType := helper.ParseCppType(fd.MapValue()) + nextOrderedMap := x.mapType(nextMapFD) + x.g.P(helper.Indent(1), "using ", orderedMapValue, " = std::pair<", nextOrderedMap, ", const ", currValueType, "*>;") + } + x.g.P(helper.Indent(1), "using ", orderedMap, " = std::map<", keyType, ", ", x.mapValueFieldType(fd), ">;") + x.g.P(helper.Indent(1), "const ", orderedMap, "* GetOrderedMap(", keys.GenGetParams(), ") const;") + x.g.P() + if depth == 1 { + x.g.P(" private:") + x.g.P(helper.Indent(1), orderedMap, " ordered_map_;") + } + return + } + } +} + +func (x *Generator) GenOrderedMapLoader() { + if !x.NeedGenerate() { + return + } + x.g.P(helper.Indent(1), "// OrderedMap init.") + x.g.P(helper.Indent(1), "ordered_map_.clear();") + x.genOrderedMapLoader(x.message.Desc, 1) +} + +func (x *Generator) genOrderedMapLoader(md protoreflect.MessageDescriptor, depth int) { + for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.IsMap() { + orderedMapValue := x.mapValueType(fd) + itemName := fmt.Sprintf("item%d", depth) + + tmpOrderedMapName := fmt.Sprintf("ordered_map%d", depth) + + prevItemName := fmt.Sprintf("item%d", depth-1) + prevContainer := prevItemName + ".second" + prevTmpOrderedMapName := fmt.Sprintf("ordered_map%d", depth-1) + if depth == 1 { + prevContainer = "data_" + prevTmpOrderedMapName = "ordered_map_" + } + x.g.P(helper.Indent(depth), "for (auto&& ", itemName, " : ", prevContainer, ".", string(fd.Name()), "()) {") + nextMapFD := getNextLevelMapFD(fd.MapValue()) + if nextMapFD != nil { + nextOrderedMap := x.mapType(nextMapFD) + x.g.P(helper.Indent(depth+1), prevTmpOrderedMapName, "[", itemName, ".first] = ", orderedMapValue, "(", nextOrderedMap, "(), &", itemName, ".second);") + x.g.P(helper.Indent(depth+1), "auto&& ", tmpOrderedMapName, " = ", prevTmpOrderedMapName, "[", itemName, ".first].first;") + } else { + ref := "&" + if fd.MapValue().Kind() != protoreflect.MessageKind { + ref = "" // scalar value type just do value copy. + } + x.g.P(helper.Indent(depth+1), prevTmpOrderedMapName, "[", itemName, ".first] = ", ref, itemName, ".second;") + } + if fd.MapValue().Kind() == protoreflect.MessageKind { + x.genOrderedMapLoader(fd.MapValue().Message(), depth+1) + } + x.g.P(helper.Indent(depth), "}") + break + } + } +} + +func (x *Generator) GenOrderedMapGetters() { + if !x.NeedGenerate() { + return + } + x.genOrderedMapGetters(x.message.Desc, 1, nil) +} + +func (x *Generator) genOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys) { + messagerName := x.messagerName() + for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.IsMap() { + orderedMap := x.mapType(fd) + + x.g.P("const ", messagerName, "::", orderedMap, "* ", messagerName, "::GetOrderedMap(", keys.GenGetParams(), ") const {") + if depth == 1 { + x.g.P(helper.Indent(1), "return &ordered_map_; ") + } else { + lastKeyName := keys[len(keys)-1].Name + prevKeys := keys[:len(keys)-1] + x.g.P(helper.Indent(1), "const auto* conf = GetOrderedMap(", prevKeys.GenGetArguments(), ");") + x.g.P(helper.Indent(1), "if (conf == nullptr) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "auto iter = conf->find(", lastKeyName, ");") + x.g.P(helper.Indent(1), "if (iter == conf->end()) {") + x.g.P(helper.Indent(2), "return nullptr;") + x.g.P(helper.Indent(1), "}") + x.g.P(helper.Indent(1), "return &iter->second.first;") + + } + x.g.P("}") + x.g.P() + + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) + if fd.MapValue().Kind() == protoreflect.MessageKind { + x.genOrderedMapGetters(fd.MapValue().Message(), depth+1, keys) + } + break + } + } +} + +func getNextLevelMapFD(fd protoreflect.FieldDescriptor) protoreflect.FieldDescriptor { + if fd.Kind() == protoreflect.MessageKind { + md := fd.Message() + for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.IsMap() { + return fd + } + } + } + return nil +} diff --git a/cmd/protoc-gen-go-tableau-loader/helper/helper.go b/cmd/protoc-gen-go-tableau-loader/helper/helper.go index 1a637829..669cca52 100644 --- a/cmd/protoc-gen-go-tableau-loader/helper/helper.go +++ b/cmd/protoc-gen-go-tableau-loader/helper/helper.go @@ -98,11 +98,11 @@ func ParseMapKeyType(fd protoreflect.FieldDescriptor) string { } } -// ParseOrderedMapKeyType converts a FieldDescriptor to its treemap key type. +// ParseOrderedIndexKeyType converts a FieldDescriptor to its treemap key type. // fd must be an ordered type, or a message which can be converted to an ordered type. -func ParseOrderedMapKeyType(fd protoreflect.FieldDescriptor) string { +func ParseOrderedIndexKeyType(gen *protogen.Plugin, g *protogen.GeneratedFile, fd protoreflect.FieldDescriptor) string { switch fd.Kind() { - case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.EnumKind: + case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: return "int32" case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: return "uint32" @@ -116,6 +116,8 @@ func ParseOrderedMapKeyType(fd protoreflect.FieldDescriptor) string { return "double" case protoreflect.StringKind: return "string" + case protoreflect.EnumKind: + return g.QualifiedGoIdent(FindEnumGoIdent(gen, fd.Enum())) case protoreflect.MessageKind: switch fd.Message().FullName() { case "google.protobuf.Timestamp", "google.protobuf.Duration": @@ -128,6 +130,25 @@ func ParseOrderedMapKeyType(fd protoreflect.FieldDescriptor) string { } } +func ParseMapFieldName(fd protoreflect.FieldDescriptor) string { + opts := fd.Options().(*descriptorpb.FieldOptions) + fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) + name := fdOpts.GetKey() + if fd.MapValue().Kind() == protoreflect.MessageKind { + valueFd := fd.MapValue().Message().Fields().Get(0) + name = string(valueFd.Name()) + } + return escapeIdentifier(name) +} + +func ParseMapValueType(gen *protogen.Plugin, g *protogen.GeneratedFile, fd protoreflect.FieldDescriptor) string { + valueType := ParseGoType(gen, g, fd.MapValue()) + if fd.MapValue().Kind() == protoreflect.MessageKind { + return "*" + valueType + } + return valueType +} + func FindMessage(gen *protogen.Plugin, md protoreflect.MessageDescriptor) *protogen.Message { if file, ok := gen.FilesByPath[md.ParentFile().Path()]; ok { return FindMessageByDescriptor(file.Messages, md) @@ -222,54 +243,41 @@ func GetTypeEmptyValue(fd protoreflect.FieldDescriptor) string { } type MapKey struct { - Type string - Name string + Type string + Name string + FieldName string // multi-colunm index only } -func AddMapKey(gen *protogen.Plugin, fd protoreflect.FieldDescriptor, keys []MapKey) []MapKey { - opts := fd.Options().(*descriptorpb.FieldOptions) - fdOpts := proto.GetExtension(opts, tableaupb.E_Field).(*tableaupb.FieldOptions) - name := fdOpts.GetKey() - if fd.MapValue().Kind() == protoreflect.MessageKind { - valueFd := fd.MapValue().Message().Fields().Get(0) - name = string(valueFd.Name()) +type MapKeys []MapKey + +func (keys MapKeys) AddMapKey(newKey MapKey) MapKeys { + if newKey.Name == "" { + newKey.Name = fmt.Sprintf("key%d", len(keys)+1) } - name = escapeIdentifier(name) - if name == "" { - name = fmt.Sprintf("key%d", len(keys)+1) - } else { - for _, key := range keys { - if key.Name == name { - // rewrite to avoid name confict - name = fmt.Sprintf("%s%d", name, len(keys)+1) - break - } + for _, key := range keys { + if key.Name == newKey.Name { + // rewrite to avoid name confict + newKey.Name = fmt.Sprintf("%s%d", newKey.Name, len(keys)+1) + break } } - keys = append(keys, MapKey{ParseMapKeyType(fd.MapKey()), name}) - return keys + return append(keys, newKey) } // GenGetParams generates function parameters, which are the names listed in the function's definition. -func GenGetParams(keys []MapKey) string { - var params string - for i, key := range keys { - params += key.Name + " " + key.Type - if i != len(keys)-1 { - params += ", " - } +func (keys MapKeys) GenGetParams() string { + var params []string + for _, key := range keys { + params = append(params, key.Name+" "+key.Type) } - return params + return strings.Join(params, ", ") } // GenGetArguments generates function arguments, which are the real values passed to the function. -func GenGetArguments(keys []MapKey) string { - var params string - for i, key := range keys { - params += key.Name - if i != len(keys)-1 { - params += ", " - } +func (keys MapKeys) GenGetArguments() string { + var params []string + for _, key := range keys { + params = append(params, key.Name) } - return params + return strings.Join(params, ", ") } diff --git a/cmd/protoc-gen-go-tableau-loader/helper/imports.go b/cmd/protoc-gen-go-tableau-loader/helper/imports.go new file mode 100644 index 00000000..6b34cfde --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/helper/imports.go @@ -0,0 +1,16 @@ +package helper + +import "google.golang.org/protobuf/compiler/protogen" + +const ( + FormatPackage = protogen.GoImportPath("github.com/tableauio/tableau/format") + LoadPackage = protogen.GoImportPath("github.com/tableauio/tableau/load") + StorePackage = protogen.GoImportPath("github.com/tableauio/tableau/store") + Errors = protogen.GoImportPath("github.com/pkg/errors") + TreeMapPackage = protogen.GoImportPath("github.com/tableauio/loader/pkg/treemap") + PairPackage = protogen.GoImportPath("github.com/tableauio/loader/pkg/pair") + TimePackage = protogen.GoImportPath("time") + SortPackage = protogen.GoImportPath("sort") + FmtPackage = protogen.GoImportPath("fmt") + ProtoPackage = protogen.GoImportPath("google.golang.org/protobuf/proto") +) diff --git a/cmd/protoc-gen-go-tableau-loader/index.go b/cmd/protoc-gen-go-tableau-loader/index.go deleted file mode 100644 index 52dbec45..00000000 --- a/cmd/protoc-gen-go-tableau-loader/index.go +++ /dev/null @@ -1,231 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" - "google.golang.org/protobuf/compiler/protogen" -) - -func genIndexTypeDef(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - g.P("// Index types.") - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take first field - g.P("// Index: ", index.Index) - mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()) - keyType := helper.ParseGoType(gen, g, field.FD) - g.P("type ", mapType, " = map[", keyType, "][]*", helper.FindMessageGoIdent(gen, index.MD)) - } else { - // multi-column index - g.P("// Index: ", index.Index) - keyType := fmt.Sprintf("%s_Index_%sKey", messagerName, index.Name()) - mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()) - - // generate key struct - // KeyType must be comparable, refer https://go.dev/blog/maps - g.P("type ", keyType, " struct {") - for _, field := range index.ColFields { - g.P(helper.ParseIndexFieldNameAsKeyStructFieldName(gen, field.FD), " ", helper.ParseGoType(gen, g, field.FD)) - } - g.P("}") - g.P("type ", mapType, " = map[", keyType, "][]*", helper.FindMessageGoIdent(gen, index.MD)) - } - g.P() - } - } -} - -func genIndexField(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" - mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()) - g.P(indexContainerName, " ", mapType) - } - } -} - -func genIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - g.P("// Index init.") - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" - g.P("x.", indexContainerName, " = make(", fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()), ")") - } - } - parentDataName := "x.data" - depth := 1 - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - genOneIndexLoader(gen, g, depth, index, parentDataName, messagerName) - } - itemName := fmt.Sprintf("item%d", depth) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGen() { - break - } - g.P("for _, ", itemName, " := range ", parentDataName, ".Get", helper.ParseIndexFieldName(gen, levelMessage.FD), "() {") - parentDataName = itemName - depth++ - } - for i := depth - 1; i > 0; i-- { - g.P("}") - } - genIndexSorter(gen, g, descriptor) -} - -func genOneIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth int, index *index.LevelIndex, - parentDataName string, messagerName string) { - indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" - g.P("{") - g.P("// Index: ", index.Index) - if len(index.ColFields) == 1 { - // single-column index - field := index.ColFields[0] // just take the first field - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - } - g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") - g.P("key := ", itemName) - g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - g.P("}") - } else { - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - } - g.P("key := ", parentDataName, fieldName) - g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - } - } else { - // multi-column index - generateOneMulticolumnIndex(gen, g, depth, index, parentDataName, messagerName, nil) - } - g.P("}") -} - -func genIndexSorter(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" - if len(index.SortedColFields) != 0 { - g.P("// Index(sort): ", index.Index) - g.P("for _, item := range x.", indexContainerName, " {") - g.P(sortPackage.Ident("Slice"), "(item, func(i, j int) bool {") - for i, field := range index.SortedColFields { - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - } - if i == len(index.SortedColFields)-1 { - g.P("return item[i]", fieldName, " < item[j]", fieldName) - } else { - g.P("if item[i]", fieldName, " != item[j]", fieldName, " {") - g.P("return item[i]", fieldName, " < item[j]", fieldName) - g.P("}") - } - } - g.P("})") - g.P("}") - } - } - } -} - -func generateOneMulticolumnIndex(gen *protogen.Plugin, g *protogen.GeneratedFile, - depth int, index *index.LevelIndex, parentDataName string, messagerName string, keys []string) []string { - cursor := len(keys) - if cursor >= len(index.ColFields) { - var keyParams string - for i, key := range keys { - keyParams += key - if i != len(keys)-1 { - keyParams += ", " - } - } - keyType := fmt.Sprintf("%s_Index_%sKey", messagerName, index.Name()) - indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" - g.P("key := ", keyType, " {", keyParams, "}") - g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") - return keys - } - field := index.ColFields[cursor] - if field.FD.IsList() { - itemName := fmt.Sprintf("indexItem%d", cursor) - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - } - g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") - keys = append(keys, itemName) - keys = generateOneMulticolumnIndex(gen, g, depth+1, index, parentDataName, messagerName, keys) - g.P("}") - } else { - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - } - key := parentDataName + fieldName - keys = append(keys, key) - keys = generateOneMulticolumnIndex(gen, g, depth, index, parentDataName, messagerName, keys) - } - return keys -} - -func genIndexFinders(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.Indexes { - mapType := fmt.Sprintf("%s_Index_%sMap", messagerName, index.Name()) - indexContainerName := "index" + strcase.ToCamel(index.Name()) + "Map" - - g.P("// Index: ", index.Index) - g.P() - - g.P("// Find", index.Name(), "Map finds the index (", index.Index, ") to value (", helper.FindMessageGoIdent(gen, index.MD), ") map.") - g.P("// One key may correspond to multiple values, which are contained by a slice.") - g.P("func (x *", messagerName, ") Find", index.Name(), "Map() ", mapType, " {") - g.P("return x.", indexContainerName) - g.P("}") - g.P() - - var keys []helper.MapKey - for _, field := range index.ColFields { - keys = append(keys, helper.MapKey{ - Type: helper.ParseGoType(gen, g, field.FD), - Name: helper.ParseIndexFieldNameAsFuncParam(gen, field.FD), - }) - } - - g.P("// Find", index.Name(), " finds a slice of all values of the given key.") - g.P("func (x *", messagerName, ") Find", index.Name(), "(", helper.GenGetParams(keys), ") []*", helper.FindMessageGoIdent(gen, index.MD), " {") - if len(index.ColFields) == 1 { - g.P("return x.", indexContainerName, "[", helper.GenGetArguments(keys), "]") - } else { - g.P("return x.", indexContainerName, "[", fmt.Sprintf("%s_Index_%sKey", messagerName, index.Name()), "{", helper.GenGetArguments(keys), "}]") - } - g.P("}") - g.P() - - g.P("// FindFirst", index.Name(), " finds the first value of the given key,") - g.P("// or nil if no value found.") - g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", helper.GenGetParams(keys), ") *", helper.FindMessageGoIdent(gen, index.MD), " {") - g.P("val := x.Find", index.Name(), "(", helper.GenGetArguments(keys), ")") - g.P("if len(val) > 0 {") - g.P("return val[0]") - g.P("}") - g.P("return nil") - g.P("}") - g.P() - } - } -} diff --git a/cmd/protoc-gen-go-tableau-loader/index/index.go b/cmd/protoc-gen-go-tableau-loader/index/index.go new file mode 100644 index 00000000..03278700 --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/index/index.go @@ -0,0 +1,272 @@ +package index + +import ( + "fmt" + + "github.com/iancoleman/strcase" + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/options" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type Generator struct { + gen *protogen.Plugin + g *protogen.GeneratedFile + descriptor *index.IndexDescriptor + message *protogen.Message +} + +func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { + return &Generator{ + gen: gen, + g: g, + descriptor: descriptor, + message: message, + } +} + +func (x *Generator) needGenerate() bool { + return options.NeedGenIndex(x.message.Desc, options.LangGO) +} + +func (x *Generator) messagerName() string { + return string(x.message.Desc.Name()) +} + +func (x *Generator) mapType(index *index.LevelIndex) string { + return fmt.Sprintf("%s_Index_%sMap", x.messagerName(), index.Name()) +} + +func (x *Generator) mapKeyType(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + return helper.ParseGoType(x.gen, x.g, field.FD) + } else { + // multi-column index + return fmt.Sprintf("%s_Index_%sKey", x.messagerName(), index.Name()) + } +} + +func (x *Generator) mapValueType(index *index.LevelIndex) protogen.GoIdent { + return helper.FindMessageGoIdent(x.gen, index.MD) +} + +func (x *Generator) indexContainerName(index *index.LevelIndex) string { + return fmt.Sprintf("index%sMap", strcase.ToCamel(index.Name())) +} + +func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeys { + var keys helper.MapKeys + for _, field := range index.ColFields { + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseGoType(x.gen, x.g, field.FD), + Name: helper.ParseIndexFieldNameAsFuncParam(x.gen, field.FD), + FieldName: helper.ParseIndexFieldNameAsKeyStructFieldName(x.gen, field.FD), + }) + } + return keys +} + +func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { + return fmt.Sprintf(".Get%s()", helper.ParseIndexFieldName(x.gen, fd)) +} + +func (x *Generator) parseKeyFieldName(field *index.LevelField) string { + var fieldName string + for _, leveledFd := range field.LeveledFDList { + fieldName += x.fieldGetter(leveledFd) + } + return fieldName +} + +func (x *Generator) GenIndexTypeDef() { + if !x.needGenerate() { + return + } + x.g.P("// Index types.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P("// Index: ", index.Index) + if len(index.ColFields) != 1 { + // multi-column index + keyType := x.mapKeyType(index) + keys := x.indexKeys(index) + + // generate key struct + // KeyType must be comparable, refer https://go.dev/blog/maps + x.g.P("type ", keyType, " struct {") + for _, key := range keys { + x.g.P(key.FieldName, " ", key.Type) + } + x.g.P("}") + } + x.g.P("type ", x.mapType(index), " = map[", x.mapKeyType(index), "][]*", x.mapValueType(index)) + x.g.P() + } + } +} + +func (x *Generator) GenIndexField() { + if !x.needGenerate() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P(x.indexContainerName(index), " ", x.mapType(index)) + } + } +} + +func (x *Generator) GenIndexLoader() { + if !x.needGenerate() { + return + } + x.g.P("// Index init.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.g.P("x.", x.indexContainerName(index), " = make(", x.mapType(index), ")") + } + } + parentDataName := "x.data" + depth := 1 + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + x.genOneIndexLoader(index, depth, parentDataName) + } + itemName := fmt.Sprintf("item%d", depth) + if levelMessage.FD == nil { + break + } + if !levelMessage.NextLevel.NeedGen() { + break + } + x.g.P("for _, ", itemName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + parentDataName = itemName + depth++ + } + for i := depth - 1; i > 0; i-- { + x.g.P("}") + } + x.genIndexSorter() +} + +func (x *Generator) genOneIndexLoader(index *index.LevelIndex, depth int, parentDataName string) { + x.g.P("{") + x.g.P("// Index: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + fieldName := x.parseKeyFieldName(field) + indexContainerName := x.indexContainerName(index) + if field.FD.IsList() { + itemName := fmt.Sprintf("item%d", depth) + x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") + x.g.P("key := ", itemName) + x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") + x.g.P("}") + } else { + x.g.P("key := ", parentDataName, fieldName) + x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") + } + } else { + // multi-column index + x.generateOneMulticolumnIndex(depth, index, parentDataName, nil) + } + x.g.P("}") +} + +func (x *Generator) generateOneMulticolumnIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeys) { + cursor := len(keys) + if cursor >= len(index.ColFields) { + keyType := x.mapKeyType(index) + indexContainerName := x.indexContainerName(index) + x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") + x.g.P("x.", indexContainerName, "[key] = append(x.", indexContainerName, "[key], ", parentDataName, ")") + return + } + field := index.ColFields[cursor] + fieldName := x.parseKeyFieldName(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("indexItem%d", cursor) + x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") + keys = keys.AddMapKey(helper.MapKey{Name: itemName}) + x.generateOneMulticolumnIndex(depth+1, index, parentDataName, keys) + x.g.P("}") + } else { + key := parentDataName + fieldName + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneMulticolumnIndex(depth, index, parentDataName, keys) + } +} + +func (x *Generator) genIndexSorter() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + if len(index.SortedColFields) != 0 { + x.g.P("// Index(sort): ", index.Index) + x.g.P("for _, item := range x.", x.indexContainerName(index), " {") + x.g.P(helper.SortPackage.Ident("Slice"), "(item, func(i, j int) bool {") + for i, field := range index.SortedColFields { + fieldName := x.parseKeyFieldName(field) + if i == len(index.SortedColFields)-1 { + x.g.P("return item[i]", fieldName, " < item[j]", fieldName) + } else { + x.g.P("if item[i]", fieldName, " != item[j]", fieldName, " {") + x.g.P("return item[i]", fieldName, " < item[j]", fieldName) + x.g.P("}") + } + } + x.g.P("})") + x.g.P("}") + } + } + } +} + +func (x *Generator) GenIndexFinders() { + if !x.needGenerate() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.Indexes { + indexContainerName := x.indexContainerName(index) + messagerName := x.messagerName() + x.g.P("// Index: ", index.Index) + x.g.P() + + x.g.P("// Find", index.Name(), "Map finds the index (", index.Index, ") to value (", x.mapValueType(index), ") map.") + x.g.P("// One key may correspond to multiple values, which are contained by a slice.") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map() ", x.mapType(index), " {") + x.g.P("return x.", indexContainerName) + x.g.P("}") + x.g.P() + + keys := x.indexKeys(index) + params := keys.GenGetParams() + args := keys.GenGetArguments() + x.g.P("// Find", index.Name(), " finds a slice of all values of the given key.") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "(", params, ") []*", x.mapValueType(index), " {") + if len(index.ColFields) == 1 { + x.g.P("return x.", indexContainerName, "[", args, "]") + } else { + x.g.P("return x.", indexContainerName, "[", x.mapKeyType(index), "{", args, "}]") + } + x.g.P("}") + x.g.P() + + x.g.P("// FindFirst", index.Name(), " finds the first value of the given key,") + x.g.P("// or nil if no value found.") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), "(", args, ")") + x.g.P("if len(val) > 0 {") + x.g.P("return val[0]") + x.g.P("}") + x.g.P("return nil") + x.g.P("}") + x.g.P() + } + } +} diff --git a/cmd/protoc-gen-go-tableau-loader/messager.go b/cmd/protoc-gen-go-tableau-loader/messager.go index add8c904..54760391 100644 --- a/cmd/protoc-gen-go-tableau-loader/messager.go +++ b/cmd/protoc-gen-go-tableau-loader/messager.go @@ -4,6 +4,9 @@ import ( "fmt" "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + idx "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/index" + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/orderedindex" + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/orderedmap" "github.com/tableauio/loader/internal/extensions" "github.com/tableauio/loader/internal/index" "github.com/tableauio/loader/internal/options" @@ -14,19 +17,6 @@ import ( "google.golang.org/protobuf/types/descriptorpb" ) -const ( - formatPackage = protogen.GoImportPath("github.com/tableauio/tableau/format") - loadPackage = protogen.GoImportPath("github.com/tableauio/tableau/load") - storePackage = protogen.GoImportPath("github.com/tableauio/tableau/store") - errors = protogen.GoImportPath("github.com/pkg/errors") - treeMapPackage = protogen.GoImportPath("github.com/tableauio/loader/pkg/treemap") - pairPackage = protogen.GoImportPath("github.com/tableauio/loader/pkg/pair") - timePackage = protogen.GoImportPath("time") - sortPackage = protogen.GoImportPath("sort") - fmtPackage = protogen.GoImportPath("fmt") - protoPackage = protogen.GoImportPath("google.golang.org/protobuf/proto") -) - // golbal container for record all proto filenames and messager names var messagers []string @@ -75,16 +65,14 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog messagerName := string(message.Desc.Name()) indexDescriptor := index.ParseIndexDescriptor(message.Desc) + orderedMapGenerator := orderedmap.NewGenerator(gen, g, message) + indexGenerator := idx.NewGenerator(gen, g, indexDescriptor, message) + orderedIndexGenerator := orderedindex.NewGenerator(gen, g, indexDescriptor, message) + // type definitions - if options.NeedGenOrderedMap(message.Desc, options.LangGO) { - genOrderedMapTypeDef(gen, g, message.Desc, 1, nil, messagerName) - } - if options.NeedGenIndex(message.Desc, options.LangGO) { - genIndexTypeDef(gen, g, indexDescriptor, messagerName) - } - if options.NeedGenOrderedIndex(message.Desc, options.LangGO) { - genOrderedIndexTypeDef(gen, g, indexDescriptor, messagerName) - } + orderedMapGenerator.GenOrderedMapTypeDef() + indexGenerator.GenIndexTypeDef() + orderedIndexGenerator.GenOrderedIndexTypeDef() g.P("// ", messagerName, " is a wrapper around protobuf message: ", message.GoIdent, ".") g.P("//") @@ -97,15 +85,9 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog g.P("type ", messagerName, " struct {") g.P("UnimplementedMessager") g.P("data, originalData *", message.GoIdent) - if options.NeedGenOrderedMap(message.Desc, options.LangGO) { - genOrderedMapField(g, message.Desc) - } - if options.NeedGenIndex(message.Desc, options.LangGO) { - genIndexField(g, indexDescriptor, messagerName) - } - if options.NeedGenOrderedIndex(message.Desc, options.LangGO) { - genOrderedIndexField(g, indexDescriptor, messagerName) - } + orderedMapGenerator.GenOrderedMapField() + indexGenerator.GenIndexField() + orderedIndexGenerator.GenOrderedIndexField() g.P("}") g.P() @@ -129,13 +111,13 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog g.P() g.P("// Load fills ", messagerName, "'s inner message from file in the specified directory and format.") - g.P("func (x *", messagerName, ") Load(dir string, format ", formatPackage.Ident("Format"), " , opts *", loadPackage.Ident("MessagerOptions"), ") error {") - g.P("start := ", timePackage.Ident("Now"), "()") + g.P("func (x *", messagerName, ") Load(dir string, format ", helper.FormatPackage.Ident("Format"), " , opts *", helper.LoadPackage.Ident("MessagerOptions"), ") error {") + g.P("start := ", helper.TimePackage.Ident("Now"), "()") g.P("defer func () {") - g.P("x.Stats.Duration = ", timePackage.Ident("Since"), "(start)") + g.P("x.Stats.Duration = ", helper.TimePackage.Ident("Since"), "(start)") g.P("}()") g.P("x.data = &", message.GoIdent, "{}") - g.P("err := ", loadPackage.Ident("LoadMessagerInDir"), "(x.data, dir, format, opts)") + g.P("err := ", helper.LoadPackage.Ident("LoadMessagerInDir"), "(x.data, dir, format, opts)") g.P("if err != nil {") g.P("return err") g.P("}") @@ -148,13 +130,13 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog g.P("// Store writes ", messagerName, "'s inner message to file in the specified directory and format.") g.P("// Available formats: JSON, Bin, and Text.") - g.P("func (x *", messagerName, ") Store(dir string, format ", formatPackage.Ident("Format"), " , options ...", storePackage.Ident("Option"), ") error {") - g.P("return ", storePackage.Ident("Store"), "(x.Data(), dir, format, options...)") + g.P("func (x *", messagerName, ") Store(dir string, format ", helper.FormatPackage.Ident("Format"), " , options ...", helper.StorePackage.Ident("Option"), ") error {") + g.P("return ", helper.StorePackage.Ident("Store"), "(x.Data(), dir, format, options...)") g.P("}") g.P() g.P("// Message returns the ", messagerName, "'s inner message data.") - g.P("func (x *", messagerName, ") Message() ", protoPackage.Ident("Message"), " {") + g.P("func (x *", messagerName, ") Message() ", helper.ProtoPackage.Ident("Message"), " {") g.P(`return x.Data()`) g.P("}") g.P() @@ -177,15 +159,9 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog if options.NeedGenOrderedMap(message.Desc, options.LangGO) || options.NeedGenIndex(message.Desc, options.LangGO) || options.NeedGenOrderedIndex(message.Desc, options.LangGO) { g.P("// processAfterLoad runs after this messager is loaded.") g.P("func (x *", messagerName, ") processAfterLoad() error {") - if options.NeedGenOrderedMap(message.Desc, options.LangGO) { - genOrderedMapLoader(gen, g, message.Desc, 1, nil, messagerName, "") - } - if options.NeedGenIndex(message.Desc, options.LangGO) { - genIndexLoader(gen, g, indexDescriptor, messagerName) - } - if options.NeedGenOrderedIndex(message.Desc, options.LangGO) { - genOrderedIndexLoader(gen, g, indexDescriptor) - } + orderedMapGenerator.GenOrderedMapLoader() + indexGenerator.GenIndexLoader() + orderedIndexGenerator.GenOrderedIndexLoader() g.P("return nil") g.P("}") g.P() @@ -193,26 +169,23 @@ func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protog // syntactic sugar for accessing map items genMapGetters(gen, g, message, 1, nil, messagerName) - if options.NeedGenOrderedMap(message.Desc, options.LangGO) { - genOrderedMapGetters(gen, g, message.Desc, 1, nil, messagerName) - } - if options.NeedGenIndex(message.Desc, options.LangGO) { - genIndexFinders(gen, g, indexDescriptor, messagerName) - } - if options.NeedGenOrderedIndex(message.Desc, options.LangGO) { - genOrderedIndexFinders(gen, g, indexDescriptor, messagerName) - } + orderedMapGenerator.GenOrderedMapGetters() + indexGenerator.GenIndexFinders() + orderedIndexGenerator.GenOrderedIndexFinders() } -func genMapGetters(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protogen.Message, depth int, keys []helper.MapKey, messagerName string) { +func genMapGetters(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protogen.Message, depth int, keys helper.MapKeys, messagerName string) { for _, field := range message.Fields { fd := field.Desc if fd.IsMap() { - keys = helper.AddMapKey(gen, fd, keys) + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) getter := fmt.Sprintf("Get%v", depth) g.P("// ", getter, " finds value in the ", depth, "-level map. It will return") g.P("// NotFound error if the key is not found.") - g.P("func (x *", messagerName, ") ", getter, "(", helper.GenGetParams(keys), ") (", parseMapValueType(gen, g, fd), ", error) {") + g.P("func (x *", messagerName, ") ", getter, "(", keys.GenGetParams(), ") (", helper.ParseMapValueType(gen, g, fd), ", error) {") returnEmptyValue := helper.GetTypeEmptyValue(fd.MapValue()) @@ -223,7 +196,7 @@ func genMapGetters(gen *protogen.Plugin, g *protogen.GeneratedFile, message *pro container = "conf" prevKeys := keys[:len(keys)-1] prevGetter := fmt.Sprintf("Get%v", depth-1) - g.P("conf, err := x.", prevGetter, "(", helper.GenGetArguments(prevKeys), ")") + g.P("conf, err := x.", prevGetter, "(", prevKeys.GenGetArguments(), ")") g.P("if err != nil {") g.P(`return `, returnEmptyValue, `, err`) g.P("}") @@ -232,7 +205,7 @@ func genMapGetters(gen *protogen.Plugin, g *protogen.GeneratedFile, message *pro g.P("d := ", container, ".Get", field.GoName, "()") lastKeyName := keys[len(keys)-1].Name g.P("if val, ok := d[", lastKeyName, "]; !ok {") - g.P(`return `, returnEmptyValue, `, `, fmtPackage.Ident("Errorf"), `("`, lastKeyName, `(%v) %w", `, lastKeyName, `, ErrNotFound)`) + g.P(`return `, returnEmptyValue, `, `, helper.FmtPackage.Ident("Errorf"), `("`, lastKeyName, `(%v) %w", `, lastKeyName, `, ErrNotFound)`) g.P("} else {") g.P(`return val, nil`) g.P("}") @@ -249,24 +222,3 @@ func genMapGetters(gen *protogen.Plugin, g *protogen.GeneratedFile, message *pro } } } - -func getNextLevelMapFD(fd protoreflect.FieldDescriptor) protoreflect.FieldDescriptor { - if fd.Kind() == protoreflect.MessageKind { - md := fd.Message() - for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() { - return fd - } - } - } - return nil -} - -func parseMapValueType(gen *protogen.Plugin, g *protogen.GeneratedFile, fd protoreflect.FieldDescriptor) string { - valueType := helper.ParseGoType(gen, g, fd.MapValue()) - if fd.MapValue().Kind() == protoreflect.MessageKind { - return "*" + valueType - } - return valueType -} diff --git a/cmd/protoc-gen-go-tableau-loader/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/ordered_index.go deleted file mode 100644 index bb947e49..00000000 --- a/cmd/protoc-gen-go-tableau-loader/ordered_index.go +++ /dev/null @@ -1,186 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - "github.com/tableauio/loader/internal/index" - "google.golang.org/protobuf/compiler/protogen" -) - -func genOrderedIndexTypeDef(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - g.P("// OrderedIndex types.") - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - // single-column index - field := index.ColFields[0] // just take first field - g.P("// OrderedIndex: ", index.Index) - mapType := fmt.Sprintf("%s_OrderedIndex_%sMap", messagerName, index.Name()) - keyType := helper.ParseOrderedMapKeyType(field.FD) - g.P("type ", mapType, " = ", treeMapPackage.Ident("TreeMap"), "[", keyType, ", []*", helper.FindMessageGoIdent(gen, index.MD), "]") - g.P() - } - } -} - -func genOrderedIndexField(g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - indexContainerName := "orderedIndex" + strcase.ToCamel(index.Name()) + "Map" - mapType := fmt.Sprintf("%s_OrderedIndex_%sMap", messagerName, index.Name()) - g.P(indexContainerName, " *", mapType) - } - } -} - -func genOrderedIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - g.P("// OrderedIndex init.") - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - field := index.ColFields[0] // just take first field - indexContainerName := "orderedIndex" + strcase.ToCamel(index.Name()) + "Map" - keyType := helper.ParseOrderedMapKeyType(field.FD) - g.P("x.", indexContainerName, " = ", treeMapPackage.Ident("New"), "[", keyType, ", []*", helper.FindMessageGoIdent(gen, index.MD), "]()") - } - } - parentDataName := "x.data" - depth := 1 - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - genOneOrderedIndexLoader(gen, g, depth, index, parentDataName) - } - itemName := fmt.Sprintf("item%d", depth) - if levelMessage.FD == nil { - break - } - if !levelMessage.NextLevel.NeedGen() { - break - } - g.P("for _, ", itemName, " := range ", parentDataName, ".Get", helper.ParseIndexFieldName(gen, levelMessage.FD), "() {") - parentDataName = itemName - depth++ - } - for i := depth - 1; i > 0; i-- { - g.P("}") - } - genOrderedIndexSorter(gen, g, descriptor) -} - -func genOneOrderedIndexLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, depth int, index *index.LevelIndex, - parentDataName string) { - indexContainerName := "orderedIndex" + strcase.ToCamel(index.Name()) + "Map" - g.P("{") - g.P("// OrderedIndex: ", index.Index) - // single-column index - field := index.ColFields[0] // just take the first field - if field.FD.IsList() { - itemName := fmt.Sprintf("item%d", depth) - fieldName := "" - suffix := "" - for i, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { - switch leveledFd.Message().FullName() { - case "google.protobuf.Timestamp", "google.protobuf.Duration": - suffix = ".GetSeconds()" - default: - } - } - } - g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") - g.P("key := ", itemName, suffix) - g.P("value, _ := x.", indexContainerName, ".Get(key)") - g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - g.P("}") - } else { - fieldName := "" - suffix := "" - for i, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { - switch leveledFd.Message().FullName() { - case "google.protobuf.Timestamp", "google.protobuf.Duration": - suffix = ".GetSeconds()" - default: - } - } - } - g.P("key := ", parentDataName, fieldName, suffix) - g.P("value, _ := x.", indexContainerName, ".Get(key)") - g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") - } - - g.P("}") -} - -func genOrderedIndexSorter(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - indexContainerName := "orderedIndex" + strcase.ToCamel(index.Name()) + "Map" - if len(index.SortedColFields) != 0 { - g.P("// OrderedIndex(sort): ", index.Index) - g.P("x.", indexContainerName, ".Range(func(key int64, item []*", helper.FindMessageGoIdent(gen, index.MD), ") bool {") - g.P(sortPackage.Ident("Slice"), "(item, func(i, j int) bool {") - for i, field := range index.SortedColFields { - fieldName := "" - for _, leveledFd := range field.LeveledFDList { - fieldName += ".Get" + helper.ParseIndexFieldName(gen, leveledFd) + "()" - } - if i == len(index.SortedColFields)-1 { - g.P("return item[i]", fieldName, " < item[j]", fieldName) - } else { - g.P("if item[i]", fieldName, " != item[j]", fieldName, " {") - g.P("return item[i]", fieldName, " < item[j]", fieldName) - g.P("}") - } - } - g.P("})") - g.P("return true") - g.P("})") - } - } - } -} - -func genOrderedIndexFinders(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, messagerName string) { - for levelMessage := descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { - for _, index := range levelMessage.OrderedIndexes { - mapType := fmt.Sprintf("%s_OrderedIndex_%sMap", messagerName, index.Name()) - indexContainerName := "orderedIndex" + strcase.ToCamel(index.Name()) + "Map" - - g.P("// OrderedIndex: ", index.Index) - g.P() - - g.P("// Find", index.Name(), "Map finds the ordered index (", index.Index, ") to value (", helper.FindMessageGoIdent(gen, index.MD), ") treemap.") - g.P("// One key may correspond to multiple values, which are contained by a slice.") - g.P("func (x *", messagerName, ") Find", index.Name(), "Map() *", mapType, " {") - g.P("return x.", indexContainerName) - g.P("}") - g.P() - - // single-column index - field := index.ColFields[0] // just take first field - keyType := helper.ParseOrderedMapKeyType(field.FD) - keyName := helper.ParseIndexFieldNameAsFuncParam(gen, field.FD) - - g.P("// Find", index.Name(), " finds a slice of all values of the given key.") - g.P("func (x *", messagerName, ") Find", index.Name(), "(", keyName, " ", keyType, ") []*", helper.FindMessageGoIdent(gen, index.MD), " {") - g.P("val, _ := x.", indexContainerName, ".Get(", keyName, ")") - g.P("return val") - g.P("}") - g.P() - - g.P("// FindFirst", index.Name(), " finds the first value of the given key,") - g.P("// or nil if no value found.") - g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", keyName, " ", keyType, ") *", helper.FindMessageGoIdent(gen, index.MD), " {") - g.P("val := x.Find", index.Name(), "(", keyName, ")") - g.P("if len(val) > 0 {") - g.P("return val[0]") - g.P("}") - g.P("return nil") - g.P("}") - g.P() - } - } -} diff --git a/cmd/protoc-gen-go-tableau-loader/ordered_map.go b/cmd/protoc-gen-go-tableau-loader/ordered_map.go deleted file mode 100644 index 707616ce..00000000 --- a/cmd/protoc-gen-go-tableau-loader/ordered_map.go +++ /dev/null @@ -1,202 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" - "github.com/tableauio/loader/internal/options" - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/reflect/protoreflect" -) - -const orderedMapSuffix = "_OrderedMap" -const orderedMapValueSuffix = "_OrderedMapValue" - -var orderedMapTypeDefMap map[string]bool = make(map[string]bool) - -func genOrderedMapTypeDef(gen *protogen.Plugin, g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys []helper.MapKey, messagerName string) { - if depth == 1 && !options.NeedGenOrderedMap(md, options.LangGO) { - return - } - for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() { - if depth == 1 { - g.P("// OrderedMap types.") - } - nextKeys := helper.AddMapKey(gen, fd, keys) - keyType := nextKeys[len(nextKeys)-1].Type - if keyType == "bool" { - keyType = "int" - } - if fd.MapValue().Kind() == protoreflect.MessageKind { - genOrderedMapTypeDef(gen, g, fd.MapValue().Message(), depth+1, nextKeys, messagerName) - } - prefix := parseOrderedMapPrefix(fd) - orderedMap := prefix + orderedMapSuffix - orderedMapValue := prefix + orderedMapValueSuffix - _, ok := orderedMapTypeDefMap[orderedMap] - if !ok { - orderedMapTypeDefMap[orderedMap] = true - nextMapFD := getNextLevelMapFD(fd.MapValue()) - if nextMapFD != nil { - currValueType := helper.FindMessageGoIdent(gen, fd.MapValue().Message()) - nextPrefix := parseOrderedMapPrefix(nextMapFD) - nextOrderedMap := nextPrefix + orderedMapSuffix - g.P("type ", orderedMapValue, "= ", pairPackage.Ident("Pair"), "[*", nextOrderedMap, ", *", currValueType, "];") - g.P("type ", orderedMap, "= ", treeMapPackage.Ident("TreeMap"), "[", keyType, ", *", orderedMapValue, "]") - g.P() - } else { - g.P("type ", orderedMap, "= ", treeMapPackage.Ident("TreeMap"), "[", keyType, ", ", parseMapValueType(gen, g, fd), "]") - g.P() - } - } - return - } - } -} - -func genOrderedMapField(g *protogen.GeneratedFile, md protoreflect.MessageDescriptor) { - if !options.NeedGenOrderedMap(md, options.LangGO) { - return - } - for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() { - g.P("orderedMap *", parseOrderedMapPrefix(fd), orderedMapSuffix) - return - } - } -} - -func genOrderedMapLoader(gen *protogen.Plugin, g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys []helper.MapKey, messagerName string, lastOrderedMapValue string) { - if depth == 1 { - g.P("// OrderedMap init.") - } - message := helper.FindMessage(gen, md) - for _, field := range message.Fields { - fd := field.Desc - if fd.IsMap() { - needConvertBool := false - if len(keys) > 0 && keys[len(keys)-1].Type == "bool" { - needConvertBool = true - } - nextKeys := helper.AddMapKey(gen, fd, keys) - keyType := nextKeys[len(nextKeys)-1].Type - needConvertBoolNext := false - if keyType == "bool" { - keyType = "int" - needConvertBoolNext = true - } - prefix := parseOrderedMapPrefix(fd) - orderedMapValue := prefix + orderedMapValueSuffix - mapName := fmt.Sprintf("x.Data().Get%s()", field.GoName) - nextMapFD := getNextLevelMapFD(fd.MapValue()) - if depth == 1 { - if nextMapFD == nil { - g.P("x.orderedMap = ", treeMapPackage.Ident("New"), "[", keyType, ", ", parseMapValueType(gen, g, fd), "]()") - } else { - g.P("x.orderedMap = ", treeMapPackage.Ident("New"), "[", keyType, ", *", orderedMapValue, "]()") - } - } - if depth != 1 { - mapName = fmt.Sprintf("v%d.Get%s()", depth-1, field.GoName) - keyName := fmt.Sprintf("k%d", depth-1) - if needConvertBool { - keyName = fmt.Sprintf("BoolToInt(%s)", keyName) - } - g.P("k", depth-1, "v := &", lastOrderedMapValue, "{") - if nextMapFD == nil { - g.P("First: ", treeMapPackage.Ident("New"), "[", keyType, ", ", parseMapValueType(gen, g, fd), "](),") - } else { - g.P("First: ", treeMapPackage.Ident("New"), "[", keyType, ", *", orderedMapValue, "](),") - } - g.P("Second: v", depth-1, ",") - g.P("}") - g.P("map", depth-1, ".Put(", keyName, ", k", depth-1, "v)") - } - g.P("for k", depth, ", v", depth, " := range ", mapName, "{") - if depth == 1 { - g.P("map", depth, " := x.orderedMap") - } else { - g.P("map", depth, " := k", depth-1, "v.First") - } - if nextMapFD != nil { - genOrderedMapLoader(gen, g, fd.MapValue().Message(), depth+1, nextKeys, messagerName, orderedMapValue) - } else { - keyName := fmt.Sprintf("k%d", depth) - if needConvertBoolNext { - keyName = fmt.Sprintf("BoolToInt(%s)", keyName) - } - g.P("map", depth, ".Put(", keyName, ", v", depth, ")") - } - g.P("}") - break - } - } -} - -func genOrderedMapGetters(gen *protogen.Plugin, g *protogen.GeneratedFile, md protoreflect.MessageDescriptor, depth int, keys []helper.MapKey, messagerName string) { - if depth == 1 && !options.NeedGenOrderedMap(md, options.LangGO) { - return - } - genGetterName := func(depth int) string { - getter := "GetOrderedMap" - if depth > 1 { - getter = fmt.Sprintf("GetOrderedMap%v", depth-1) - } - return getter - } - for i := 0; i < md.Fields().Len(); i++ { - fd := md.Fields().Get(i) - if fd.IsMap() { - getter := genGetterName(depth) - prefix := parseOrderedMapPrefix(fd) - orderedMap := prefix + orderedMapSuffix - if depth == 1 { - g.P("// ", getter, " returns the 1-level ordered map.") - g.P("func (x *", messagerName, ") ", getter, "(", helper.GenGetParams(keys), ") *", orderedMap, "{") - g.P("return x.orderedMap ") - } else { - g.P("// ", getter, " finds value in the ", depth-1, "-level ordered map. It will return") - g.P("// NotFound error if the key is not found.") - g.P("func (x *", messagerName, ") ", getter, "(", helper.GenGetParams(keys), ") (*", orderedMap, ", error) {") - if depth == 2 { - g.P("conf := x.orderedMap") - } else { - prevKeys := keys[:len(keys)-1] - prevGetter := genGetterName(depth - 1) - g.P("conf, err := x.", prevGetter, "(", helper.GenGetArguments(prevKeys), ")") - g.P("if err != nil {") - g.P(`return nil, err`) - g.P("}") - } - lastKeyName := keys[len(keys)-1].Name - lastKeyType := keys[len(keys)-1].Type - keyName := lastKeyName - if lastKeyType == "bool" { - keyName = fmt.Sprintf("BoolToInt(%s)", keyName) - } - g.P("if val, ok := conf.Get(", keyName, "); !ok {") - g.P(`return nil, `, fmtPackage.Ident("Errorf"), `("`, lastKeyName, `(%v) %w", `, lastKeyName, `, ErrNotFound)`) - g.P("} else {") - g.P(`return val.First, nil`) - g.P("}") - - } - g.P("}") - g.P() - - nextKeys := helper.AddMapKey(gen, fd, keys) - if fd.MapValue().Kind() == protoreflect.MessageKind { - genOrderedMapGetters(gen, g, fd.MapValue().Message(), depth+1, nextKeys, messagerName) - } - break - } - } -} - -func parseOrderedMapPrefix(mapFd protoreflect.FieldDescriptor) string { - return strcase.ToCamel(string(mapFd.FullName())) -} diff --git a/cmd/protoc-gen-go-tableau-loader/orderedindex/ordered_index.go b/cmd/protoc-gen-go-tableau-loader/orderedindex/ordered_index.go new file mode 100644 index 00000000..96f619e0 --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/orderedindex/ordered_index.go @@ -0,0 +1,309 @@ +package orderedindex + +import ( + "fmt" + + "github.com/iancoleman/strcase" + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + "github.com/tableauio/loader/internal/index" + "github.com/tableauio/loader/internal/options" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type Generator struct { + gen *protogen.Plugin + g *protogen.GeneratedFile + descriptor *index.IndexDescriptor + message *protogen.Message +} + +func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, descriptor *index.IndexDescriptor, message *protogen.Message) *Generator { + return &Generator{ + gen: gen, + g: g, + descriptor: descriptor, + message: message, + } +} + +func (x *Generator) needGenerate() bool { + return options.NeedGenOrderedIndex(x.message.Desc, options.LangGO) +} + +func (x *Generator) messagerName() string { + return string(x.message.Desc.Name()) +} + +func (x *Generator) mapType(index *index.LevelIndex) string { + return fmt.Sprintf("%s_OrderedIndex_%sMap", x.messagerName(), index.Name()) +} + +func (x *Generator) mapKeyType(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take first field + return helper.ParseOrderedIndexKeyType(x.gen, x.g, field.FD) + } else { + // multi-column index + return fmt.Sprintf("%s_OrderedIndex_%sKey", x.messagerName(), index.Name()) + } +} + +func (x *Generator) mapValueType(index *index.LevelIndex) protogen.GoIdent { + return helper.FindMessageGoIdent(x.gen, index.MD) +} + +func (x *Generator) indexContainerName(index *index.LevelIndex) string { + return fmt.Sprintf("orderedIndex%sMap", strcase.ToCamel(index.Name())) +} + +func (x *Generator) mapCtor(index *index.LevelIndex) string { + if len(index.ColFields) == 1 { + // single-column index + return "New" + } else { + // multi-column index + return "New2" + } +} + +func (x *Generator) indexKeys(index *index.LevelIndex) helper.MapKeys { + var keys helper.MapKeys + for _, field := range index.ColFields { + keys = keys.AddMapKey(helper.MapKey{ + Type: helper.ParseOrderedIndexKeyType(x.gen, x.g, field.FD), + Name: helper.ParseIndexFieldNameAsFuncParam(x.gen, field.FD), + FieldName: helper.ParseIndexFieldNameAsKeyStructFieldName(x.gen, field.FD), + }) + } + return keys +} + +func (x *Generator) fieldGetter(fd protoreflect.FieldDescriptor) string { + return fmt.Sprintf(".Get%s()", helper.ParseIndexFieldName(x.gen, fd)) +} + +func (x *Generator) parseKeyFieldNameAndSuffix(field *index.LevelField) (string, string) { + var fieldName, suffix string + for i, leveledFd := range field.LeveledFDList { + fieldName += x.fieldGetter(leveledFd) + if i == len(field.LeveledFDList)-1 && leveledFd.Message() != nil { + switch leveledFd.Message().FullName() { + case "google.protobuf.Timestamp", "google.protobuf.Duration": + suffix = ".GetSeconds()" + default: + } + } + } + return fieldName, suffix +} + +func (x *Generator) GenOrderedIndexTypeDef() { + if !x.needGenerate() { + return + } + x.g.P("// OrderedIndex types.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P("// OrderedIndex: ", index.Index) + if len(index.ColFields) != 1 { + // multi-column index + keyType := x.mapKeyType(index) + keys := x.indexKeys(index) + + // generate key struct + x.g.P("type ", keyType, " struct {") + for _, key := range keys { + x.g.P(key.FieldName, " ", key.Type) + } + x.g.P("}") + x.g.P() + + // generate Less func to implement cmp.Ordered interface + x.g.P("func (x ", keyType, ") Less(other ", keyType, ") bool {") + for i, key := range keys { + if i == len(keys)-1 { + x.g.P("return x.", key.FieldName, " < other.", key.FieldName) + } else { + x.g.P("if x.", key.FieldName, " != other.", key.FieldName, " {") + x.g.P("return x.", key.FieldName, " < other.", key.FieldName) + x.g.P("}") + } + } + x.g.P("}") + x.g.P() + } + x.g.P("type ", x.mapType(index), " = ", helper.TreeMapPackage.Ident("TreeMap"), "[", x.mapKeyType(index), ", []*", x.mapValueType(index), "]") + x.g.P() + } + } +} + +func (x *Generator) GenOrderedIndexField() { + if !x.needGenerate() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P(x.indexContainerName(index), " *", x.mapType(index)) + } + } +} + +func (x *Generator) GenOrderedIndexLoader() { + if !x.needGenerate() { + return + } + x.g.P("// OrderedIndex init.") + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.g.P("x.", x.indexContainerName(index), " = ", helper.TreeMapPackage.Ident(x.mapCtor(index)), "[", x.mapKeyType(index), ", []*", x.mapValueType(index), "]()") + } + } + parentDataName := "x.data" + depth := 1 + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + x.genOneOrderedIndexLoader(index, depth, parentDataName) + } + itemName := fmt.Sprintf("item%d", depth) + if levelMessage.FD == nil { + break + } + if !levelMessage.NextLevel.NeedGen() { + break + } + x.g.P("for _, ", itemName, " := range ", parentDataName, x.fieldGetter(levelMessage.FD), " {") + parentDataName = itemName + depth++ + } + for i := depth - 1; i > 0; i-- { + x.g.P("}") + } + x.genOrderedIndexSorter() +} + +func (x *Generator) genOneOrderedIndexLoader(index *index.LevelIndex, depth int, parentDataName string) { + x.g.P("{") + x.g.P("// OrderedIndex: ", index.Index) + if len(index.ColFields) == 1 { + // single-column index + field := index.ColFields[0] // just take the first field + fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) + indexContainerName := x.indexContainerName(index) + if field.FD.IsList() { + itemName := fmt.Sprintf("item%d", depth) + x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") + x.g.P("key := ", itemName, suffix) + x.g.P("value, _ := x.", indexContainerName, ".Get(key)") + x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") + x.g.P("}") + } else { + x.g.P("key := ", parentDataName, fieldName, suffix) + x.g.P("value, _ := x.", indexContainerName, ".Get(key)") + x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") + } + } else { + // multi-column index + x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, nil) + } + x.g.P("}") +} + +func (x *Generator) generateOneMulticolumnOrderedIndex(depth int, index *index.LevelIndex, parentDataName string, keys helper.MapKeys) { + cursor := len(keys) + if cursor >= len(index.ColFields) { + keyType := x.mapKeyType(index) + indexContainerName := x.indexContainerName(index) + x.g.P("key := ", keyType, " {", keys.GenGetArguments(), "}") + x.g.P("value, _ := x.", indexContainerName, ".Get(key)") + x.g.P("x.", indexContainerName, ".Put(key, append(value, ", parentDataName, "))") + return + } + field := index.ColFields[cursor] + fieldName, suffix := x.parseKeyFieldNameAndSuffix(field) + if field.FD.IsList() { + itemName := fmt.Sprintf("indexItem%d", cursor) + x.g.P("for _, ", itemName, " := range ", parentDataName, fieldName, " {") + key := itemName + suffix + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneMulticolumnOrderedIndex(depth+1, index, parentDataName, keys) + x.g.P("}") + } else { + key := parentDataName + fieldName + suffix + keys = keys.AddMapKey(helper.MapKey{Name: key}) + x.generateOneMulticolumnOrderedIndex(depth, index, parentDataName, keys) + } +} + +func (x *Generator) genOrderedIndexSorter() { + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + if len(index.SortedColFields) != 0 { + x.g.P("// OrderedIndex(sort): ", index.Index) + x.g.P("x.", x.indexContainerName(index), ".Range(func(key ", x.mapKeyType(index), ", item []*", x.mapValueType(index), ") bool {") + x.g.P(helper.SortPackage.Ident("Slice"), "(item, func(i, j int) bool {") + for i, field := range index.SortedColFields { + fieldName, _ := x.parseKeyFieldNameAndSuffix(field) + if i == len(index.SortedColFields)-1 { + x.g.P("return item[i]", fieldName, " < item[j]", fieldName) + } else { + x.g.P("if item[i]", fieldName, " != item[j]", fieldName, " {") + x.g.P("return item[i]", fieldName, " < item[j]", fieldName) + x.g.P("}") + } + } + x.g.P("})") + x.g.P("return true") + x.g.P("})") + } + } + } +} + +func (x *Generator) GenOrderedIndexFinders() { + if !x.needGenerate() { + return + } + for levelMessage := x.descriptor.LevelMessage; levelMessage != nil; levelMessage = levelMessage.NextLevel { + for _, index := range levelMessage.OrderedIndexes { + indexContainerName := x.indexContainerName(index) + messagerName := x.messagerName() + x.g.P("// OrderedIndex: ", index.Index) + x.g.P() + + x.g.P("// Find", index.Name(), "Map finds the ordered index (", index.Index, ") to value (", x.mapValueType(index), ") treemap.") + x.g.P("// One key may correspond to multiple values, which are contained by a slice.") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "Map() *", x.mapType(index), " {") + x.g.P("return x.", indexContainerName) + x.g.P("}") + x.g.P() + + keys := x.indexKeys(index) + params := keys.GenGetParams() + args := keys.GenGetArguments() + x.g.P("// Find", index.Name(), " finds a slice of all values of the given key.") + x.g.P("func (x *", messagerName, ") Find", index.Name(), "(", params, ") []*", x.mapValueType(index), " {") + if len(index.ColFields) == 1 { + x.g.P("val, _ := x.", indexContainerName, ".Get(", args, ")") + } else { + x.g.P("val, _ := x.", indexContainerName, ".Get(", x.mapKeyType(index), "{", args, "})") + } + x.g.P("return val") + x.g.P("}") + x.g.P() + + x.g.P("// FindFirst", index.Name(), " finds the first value of the given key,") + x.g.P("// or nil if no value found.") + x.g.P("func (x *", messagerName, ") FindFirst", index.Name(), "(", params, ") *", x.mapValueType(index), " {") + x.g.P("val := x.Find", index.Name(), "(", args, ")") + x.g.P("if len(val) > 0 {") + x.g.P("return val[0]") + x.g.P("}") + x.g.P("return nil") + x.g.P("}") + x.g.P() + } + } +} diff --git a/cmd/protoc-gen-go-tableau-loader/orderedmap/ordered_map.go b/cmd/protoc-gen-go-tableau-loader/orderedmap/ordered_map.go new file mode 100644 index 00000000..6ad81ea3 --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/orderedmap/ordered_map.go @@ -0,0 +1,250 @@ +package orderedmap + +import ( + "fmt" + "strings" + + "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + "github.com/tableauio/loader/internal/options" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type Generator struct { + gen *protogen.Plugin + g *protogen.GeneratedFile + message *protogen.Message +} + +func NewGenerator(gen *protogen.Plugin, g *protogen.GeneratedFile, message *protogen.Message) *Generator { + return &Generator{ + gen: gen, + g: g, + message: message, + } +} + +func (x *Generator) needGenerate() bool { + return options.NeedGenOrderedMap(x.message.Desc, options.LangGO) +} + +func (x *Generator) messagerName() string { + return string(x.message.Desc.Name()) +} + +func (x *Generator) orderedMapPrefix(mapFd protoreflect.FieldDescriptor) string { + if mapFd.MapValue().Kind() == protoreflect.MessageKind { + localMsgProtoName := strings.TrimPrefix(string(mapFd.MapValue().Message().FullName()), string(x.message.Desc.FullName())+".") + return strings.ReplaceAll(localMsgProtoName, ".", "_") + } + return mapFd.MapValue().Kind().String() +} + +func (x *Generator) mapType(mapFd protoreflect.FieldDescriptor) string { + return fmt.Sprintf("%s_OrderedMap_%sMap", x.messagerName(), x.orderedMapPrefix(mapFd)) +} + +func (x *Generator) mapValueType(mapFd protoreflect.FieldDescriptor) string { + return fmt.Sprintf("%s_OrderedMap_%sValue", x.messagerName(), x.orderedMapPrefix(mapFd)) +} + +func (x *Generator) mapValueFieldType(fd protoreflect.FieldDescriptor) string { + nextMapFD := getNextLevelMapFD(fd.MapValue()) + if nextMapFD != nil { + return "*" + x.mapValueType(fd) + } + return helper.ParseMapValueType(x.gen, x.g, fd) +} + +func (x *Generator) GenOrderedMapTypeDef() { + if !x.needGenerate() { + return + } + x.g.P("// OrderedMap types.") + x.genOrderedMapTypeDef(x.message.Desc, 1, nil) +} + +func (x *Generator) genOrderedMapTypeDef(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys) { + for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.IsMap() { + nextKeys := keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) + keyType := nextKeys[len(nextKeys)-1].Type + if keyType == "bool" { + keyType = "int" + } + if fd.MapValue().Kind() == protoreflect.MessageKind { + x.genOrderedMapTypeDef(fd.MapValue().Message(), depth+1, nextKeys) + } + orderedMap := x.mapType(fd) + orderedMapValue := x.mapValueType(fd) + nextMapFD := getNextLevelMapFD(fd.MapValue()) + if nextMapFD != nil { + currValueType := helper.FindMessageGoIdent(x.gen, fd.MapValue().Message()) + nextOrderedMap := x.mapType(nextMapFD) + x.g.P("type ", orderedMapValue, "= ", helper.PairPackage.Ident("Pair"), "[*", nextOrderedMap, ", *", currValueType, "];") + } + x.g.P("type ", orderedMap, "= ", helper.TreeMapPackage.Ident("TreeMap"), "[", keyType, ", ", x.mapValueFieldType(fd), "]") + x.g.P() + return + } + } +} + +func (x *Generator) GenOrderedMapField() { + if !x.needGenerate() { + return + } + md := x.message.Desc + for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.IsMap() { + x.g.P("orderedMap *", x.mapType(fd)) + return + } + } +} + +func (x *Generator) GenOrderedMapLoader() { + if !x.needGenerate() { + return + } + x.g.P("// OrderedMap init.") + x.genOrderedMapLoader(x.message.Desc, 1, nil, "") +} + +func (x *Generator) genOrderedMapLoader(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys, lastOrderedMapValue string) { + message := helper.FindMessage(x.gen, md) + for _, field := range message.Fields { + fd := field.Desc + if fd.IsMap() { + needConvertBool := len(keys) > 0 && keys[len(keys)-1].Type == "int" + nextKeys := keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) + keyType := nextKeys[len(nextKeys)-1].Type + needConvertBoolNext := keyType == "bool" + if keyType == "bool" { + keyType = "int" + } + orderedMapValue := x.mapValueType(fd) + mapName := fmt.Sprintf("x.Data().Get%s()", field.GoName) + nextMapFD := getNextLevelMapFD(fd.MapValue()) + if depth == 1 { + x.g.P("x.orderedMap = ", helper.TreeMapPackage.Ident("New"), "[", keyType, ", ", x.mapValueFieldType(fd), "]()") + } + if depth != 1 { + mapName = fmt.Sprintf("v%d.Get%s()", depth-1, field.GoName) + keyName := fmt.Sprintf("k%d", depth-1) + if needConvertBool { + keyName = fmt.Sprintf("BoolToInt(%s)", keyName) + } + x.g.P("k", depth-1, "v := &", lastOrderedMapValue, "{") + x.g.P("First: ", helper.TreeMapPackage.Ident("New"), "[", keyType, ", ", x.mapValueFieldType(fd), "](),") + x.g.P("Second: v", depth-1, ",") + x.g.P("}") + x.g.P("map", depth-1, ".Put(", keyName, ", k", depth-1, "v)") + } + x.g.P("for k", depth, ", v", depth, " := range ", mapName, "{") + if depth == 1 { + x.g.P("map", depth, " := x.orderedMap") + } else { + x.g.P("map", depth, " := k", depth-1, "v.First") + } + if nextMapFD != nil { + x.genOrderedMapLoader(fd.MapValue().Message(), depth+1, nextKeys, orderedMapValue) + } else { + keyName := fmt.Sprintf("k%d", depth) + if needConvertBoolNext { + keyName = fmt.Sprintf("BoolToInt(%s)", keyName) + } + x.g.P("map", depth, ".Put(", keyName, ", v", depth, ")") + } + x.g.P("}") + break + } + } +} + +func (x *Generator) GenOrderedMapGetters() { + if !x.needGenerate() { + return + } + x.genOrderedMapGetters(x.message.Desc, 1, nil) +} + +func (x *Generator) genOrderedMapGetters(md protoreflect.MessageDescriptor, depth int, keys helper.MapKeys) { + genGetterName := func(depth int) string { + getter := "GetOrderedMap" + if depth > 1 { + getter = fmt.Sprintf("GetOrderedMap%v", depth-1) + } + return getter + } + for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.IsMap() { + getter := genGetterName(depth) + orderedMap := x.mapType(fd) + if depth == 1 { + x.g.P("// ", getter, " returns the 1-level ordered map.") + x.g.P("func (x *", x.messagerName(), ") ", getter, "(", keys.GenGetParams(), ") *", orderedMap, "{") + x.g.P("return x.orderedMap ") + } else { + x.g.P("// ", getter, " finds value in the ", depth-1, "-level ordered map. It will return") + x.g.P("// NotFound error if the key is not found.") + x.g.P("func (x *", x.messagerName(), ") ", getter, "(", keys.GenGetParams(), ") (*", orderedMap, ", error) {") + if depth == 2 { + x.g.P("conf := x.orderedMap") + } else { + prevKeys := keys[:len(keys)-1] + prevGetter := genGetterName(depth - 1) + x.g.P("conf, err := x.", prevGetter, "(", prevKeys.GenGetArguments(), ")") + x.g.P("if err != nil {") + x.g.P(`return nil, err`) + x.g.P("}") + } + lastKeyName := keys[len(keys)-1].Name + lastKeyType := keys[len(keys)-1].Type + keyName := lastKeyName + if lastKeyType == "int" { + keyName = fmt.Sprintf("BoolToInt(%s)", keyName) + } + x.g.P("if val, ok := conf.Get(", keyName, "); !ok {") + x.g.P(`return nil, `, helper.FmtPackage.Ident("Errorf"), `("`, lastKeyName, `(%v) %w", `, lastKeyName, `, ErrNotFound)`) + x.g.P("} else {") + x.g.P(`return val.First, nil`) + x.g.P("}") + + } + x.g.P("}") + x.g.P() + + nextKeys := keys.AddMapKey(helper.MapKey{ + Type: helper.ParseMapKeyType(fd.MapKey()), + Name: helper.ParseMapFieldName(fd), + }) + if fd.MapValue().Kind() == protoreflect.MessageKind { + x.genOrderedMapGetters(fd.MapValue().Message(), depth+1, nextKeys) + } + break + } + } +} + +func getNextLevelMapFD(fd protoreflect.FieldDescriptor) protoreflect.FieldDescriptor { + if fd.Kind() == protoreflect.MessageKind { + md := fd.Message() + for i := 0; i < md.Fields().Len(); i++ { + fd := md.Fields().Get(i) + if fd.IsMap() { + return fd + } + } + } + return nil +} diff --git a/internal/index/descriptor.go b/internal/index/descriptor.go index f6fb4eaf..d5e808d6 100644 --- a/internal/index/descriptor.go +++ b/internal/index/descriptor.go @@ -107,10 +107,7 @@ func parseRecursively(index *Index, prefix string, md protoreflect.MessageDescri SortedColFields: sortedColFields, } if ordered { - if len(colFields) == 1 { - // only support one column ordered index - levelMessage.OrderedIndexes = append(levelMessage.OrderedIndexes, levelIndex) - } + levelMessage.OrderedIndexes = append(levelMessage.OrderedIndexes, levelIndex) } else { levelMessage.Indexes = append(levelMessage.Indexes, levelIndex) } diff --git a/test/cpp-tableau-loader/src/main.cpp b/test/cpp-tableau-loader/src/main.cpp index 5a69857b..d532e0ed 100644 --- a/test/cpp-tableau-loader/src/main.cpp +++ b/test/cpp-tableau-loader/src/main.cpp @@ -185,7 +185,7 @@ int main() { // std::cout << section_conf->DebugString() << std::endl; const auto* chapter_ordered_map = - Hub::Instance().GetOrderedMap( + Hub::Instance().GetOrderedMap( 100001); if (!chapter_ordered_map) { ATOM_ERROR("ActivityConf GetOrderedMap chapter failed!"); @@ -209,7 +209,8 @@ int main() { } const auto* rank_ordered_map = - Hub::Instance().GetOrderedMap(100001, 1, 2); + Hub::Instance().GetOrderedMap(100001, 1, + 2); if (!rank_ordered_map) { ATOM_ERROR("ActivityConf GetOrderedMap rank failed!"); return 1; diff --git a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc index e7e530e9..3a60358a 100644 --- a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc @@ -24,7 +24,7 @@ bool HeroConf::ProcessAfterLoad() { // OrderedMap init. ordered_map_.clear(); for (auto&& item1 : data_.hero_map()) { - ordered_map_[item1.first] = Hero_OrderedMapValue(Hero_Attr_OrderedMap(), &item1.second); + ordered_map_[item1.first] = OrderedMap_HeroValue(OrderedMap_Hero_AttrMap(), &item1.second); auto&& ordered_map1 = ordered_map_[item1.first].first; for (auto&& item2 : item1.second.attr_map()) { ordered_map1[item2.first] = &item2.second; @@ -53,11 +53,11 @@ const protoconf::HeroConf::Hero::Attr* HeroConf::Get(const std::string& name, co return &iter->second; } -const HeroConf::Hero_OrderedMap* HeroConf::GetOrderedMap() const { +const HeroConf::OrderedMap_HeroMap* HeroConf::GetOrderedMap() const { return &ordered_map_; } -const HeroConf::Hero_Attr_OrderedMap* HeroConf::GetOrderedMap(const std::string& name) const { +const HeroConf::OrderedMap_Hero_AttrMap* HeroConf::GetOrderedMap(const std::string& name) const { const auto* conf = GetOrderedMap(); if (conf == nullptr) { return nullptr; diff --git a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.h index 4e5a06d8..8c3204d4 100644 --- a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.h @@ -33,15 +33,15 @@ class HeroConf : public Messager { // OrderedMap accessers. public: - using Hero_Attr_OrderedMap = std::map; - const Hero_Attr_OrderedMap* GetOrderedMap(const std::string& name) const; + using OrderedMap_Hero_AttrMap = std::map; + const OrderedMap_Hero_AttrMap* GetOrderedMap(const std::string& name) const; - using Hero_OrderedMapValue = std::pair; - using Hero_OrderedMap = std::map; - const Hero_OrderedMap* GetOrderedMap() const; + using OrderedMap_HeroValue = std::pair; + using OrderedMap_HeroMap = std::map; + const OrderedMap_HeroMap* GetOrderedMap() const; private: - Hero_OrderedMap ordered_map_; + OrderedMap_HeroMap ordered_map_; }; class HeroBaseConf : public Messager { 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 027766d8..2e3ceff6 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc @@ -108,6 +108,33 @@ bool ItemConf::ProcessAfterLoad() { return a->use_effect().type() < b->use_effect().type(); }); } + // OrderedIndex init. + ordered_index_ext_type_map_.clear(); + ordered_index_param_ext_type_map_.clear(); + for (auto&& item1 : data_.item_map()) { + { + // OrderedIndex: ExtType@ExtType + for (auto&& item2 : item1.second.ext_type_list()) { + ordered_index_ext_type_map_[static_cast(item2)].push_back(&item1.second); + } + } + { + // OrderedIndex: (Param,ExtType)@ParamExtType + for (auto&& index_item0 : item1.second.param_list()) { + for (auto&& index_item1 : item1.second.ext_type_list()) { + OrderedIndex_ParamExtTypeKey key{index_item0, static_cast(index_item1)}; + ordered_index_param_ext_type_map_[key].push_back(&item1.second); + } + } + } + } + // OrderedIndex(sort): (Param,ExtType)@ParamExtType + for (auto&& item : ordered_index_param_ext_type_map_) { + std::sort(item.second.begin(), item.second.end(), + [](const protoconf::ItemConf::Item* a, const protoconf::ItemConf::Item* b) { + return a->id() < b->id(); + }); + } return true; } @@ -119,12 +146,12 @@ const protoconf::ItemConf::Item* ItemConf::Get(uint32_t id) const { return &iter->second; } -const ItemConf::Item_OrderedMap* ItemConf::GetOrderedMap() const { +const ItemConf::OrderedMap_ItemMap* ItemConf::GetOrderedMap() const { return &ordered_map_; } // Index: Type -const ItemConf::Index_ItemMap& ItemConf::FindItemMap() const { return index_item_map_ ;} +const ItemConf::Index_ItemMap& ItemConf::FindItem() const { return index_item_map_ ;} const ItemConf::Index_ItemVector* ItemConf::FindItem(protoconf::FruitType type) const { auto iter = index_item_map_.find(type); @@ -143,7 +170,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItem(protoconf::FruitType ty } // Index: Param@ItemInfo -const ItemConf::Index_ItemInfoMap& ItemConf::FindItemInfoMap() const { return index_item_info_map_ ;} +const ItemConf::Index_ItemInfoMap& ItemConf::FindItemInfo() const { return index_item_info_map_ ;} const ItemConf::Index_ItemInfoVector* ItemConf::FindItemInfo(int32_t param) const { auto iter = index_item_info_map_.find(param); @@ -162,7 +189,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemInfo(int32_t param) cons } // Index: Default@ItemDefaultInfo -const ItemConf::Index_ItemDefaultInfoMap& ItemConf::FindItemDefaultInfoMap() const { return index_item_default_info_map_ ;} +const ItemConf::Index_ItemDefaultInfoMap& ItemConf::FindItemDefaultInfo() const { return index_item_default_info_map_ ;} const ItemConf::Index_ItemDefaultInfoVector* ItemConf::FindItemDefaultInfo(const std::string& default_) const { auto iter = index_item_default_info_map_.find(default_); @@ -181,7 +208,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemDefaultInfo(const std::s } // Index: ExtType@ItemExtInfo -const ItemConf::Index_ItemExtInfoMap& ItemConf::FindItemExtInfoMap() const { return index_item_ext_info_map_ ;} +const ItemConf::Index_ItemExtInfoMap& ItemConf::FindItemExtInfo() const { return index_item_ext_info_map_ ;} const ItemConf::Index_ItemExtInfoVector* ItemConf::FindItemExtInfo(protoconf::FruitType ext_type) const { auto iter = index_item_ext_info_map_.find(ext_type); @@ -200,7 +227,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemExtInfo(protoconf::Fruit } // Index: (ID,Name)@AwardItem -const ItemConf::Index_AwardItemMap& ItemConf::FindAwardItemMap() const { return index_award_item_map_ ;} +const ItemConf::Index_AwardItemMap& ItemConf::FindAwardItem() const { return index_award_item_map_ ;} const ItemConf::Index_AwardItemVector* ItemConf::FindAwardItem(uint32_t id, const std::string& name) const { auto iter = index_award_item_map_.find({id, name}); @@ -219,7 +246,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstAwardItem(uint32_t id, const } // Index: (ID,Type,Param,ExtType)@SpecialItem -const ItemConf::Index_SpecialItemMap& ItemConf::FindSpecialItemMap() const { return index_special_item_map_ ;} +const ItemConf::Index_SpecialItemMap& ItemConf::FindSpecialItem() const { return index_special_item_map_ ;} const ItemConf::Index_SpecialItemVector* ItemConf::FindSpecialItem(uint32_t id, protoconf::FruitType type, int32_t param, protoconf::FruitType ext_type) const { auto iter = index_special_item_map_.find({id, type, param, ext_type}); @@ -238,7 +265,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstSpecialItem(uint32_t id, pro } // Index: PathDir@ItemPathDir -const ItemConf::Index_ItemPathDirMap& ItemConf::FindItemPathDirMap() const { return index_item_path_dir_map_ ;} +const ItemConf::Index_ItemPathDirMap& ItemConf::FindItemPathDir() const { return index_item_path_dir_map_ ;} const ItemConf::Index_ItemPathDirVector* ItemConf::FindItemPathDir(const std::string& dir) const { auto iter = index_item_path_dir_map_.find(dir); @@ -257,7 +284,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemPathDir(const std::strin } // Index: PathName@ItemPathName -const ItemConf::Index_ItemPathNameMap& ItemConf::FindItemPathNameMap() const { return index_item_path_name_map_ ;} +const ItemConf::Index_ItemPathNameMap& ItemConf::FindItemPathName() const { return index_item_path_name_map_ ;} const ItemConf::Index_ItemPathNameVector* ItemConf::FindItemPathName(const std::string& name) const { auto iter = index_item_path_name_map_.find(name); @@ -276,7 +303,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemPathName(const std::stri } // Index: PathFriendID@ItemPathFriendID -const ItemConf::Index_ItemPathFriendIDMap& ItemConf::FindItemPathFriendIDMap() const { return index_item_path_friend_id_map_ ;} +const ItemConf::Index_ItemPathFriendIDMap& ItemConf::FindItemPathFriendID() const { return index_item_path_friend_id_map_ ;} const ItemConf::Index_ItemPathFriendIDVector* ItemConf::FindItemPathFriendID(uint32_t id) const { auto iter = index_item_path_friend_id_map_.find(id); @@ -295,7 +322,7 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstItemPathFriendID(uint32_t id } // Index: UseEffectType@UseEffectType -const ItemConf::Index_UseEffectTypeMap& ItemConf::FindUseEffectTypeMap() const { return index_use_effect_type_map_ ;} +const ItemConf::Index_UseEffectTypeMap& ItemConf::FindUseEffectType() const { return index_use_effect_type_map_ ;} const ItemConf::Index_UseEffectTypeVector* ItemConf::FindUseEffectType(protoconf::UseEffect::Type type) const { auto iter = index_use_effect_type_map_.find(type); @@ -313,5 +340,42 @@ const protoconf::ItemConf::Item* ItemConf::FindFirstUseEffectType(protoconf::Use return conf->front(); } +// OrderedIndex: ExtType@ExtType +const ItemConf::OrderedIndex_ExtTypeMap& ItemConf::FindExtType() const { return ordered_index_ext_type_map_ ;} + +const ItemConf::OrderedIndex_ExtTypeVector* ItemConf::FindExtType(protoconf::FruitType ext_type) const { + auto iter = ordered_index_ext_type_map_.find(ext_type); + if (iter == ordered_index_ext_type_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::ItemConf::Item* ItemConf::FindFirstExtType(protoconf::FruitType ext_type) const { + auto conf = FindExtType(ext_type); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} + +// OrderedIndex: (Param,ExtType)@ParamExtType +const ItemConf::OrderedIndex_ParamExtTypeMap& ItemConf::FindParamExtType() const { return ordered_index_param_ext_type_map_ ;} + +const ItemConf::OrderedIndex_ParamExtTypeVector* ItemConf::FindParamExtType(int32_t param, protoconf::FruitType ext_type) const { + auto iter = ordered_index_param_ext_type_map_.find({param, ext_type}); + if (iter == ordered_index_param_ext_type_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::ItemConf::Item* ItemConf::FindFirstParamExtType(int32_t param, protoconf::FruitType ext_type) const { + auto conf = FindParamExtType(param, ext_type); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} } // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h index 812d81eb..56d1bff8 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h @@ -32,11 +32,11 @@ class ItemConf : public Messager { // OrderedMap accessers. public: - using Item_OrderedMap = std::map; - const Item_OrderedMap* GetOrderedMap() const; + using OrderedMap_ItemMap = std::map; + const OrderedMap_ItemMap* GetOrderedMap() const; private: - Item_OrderedMap ordered_map_; + OrderedMap_ItemMap ordered_map_; // Index accessers. // Index: Type @@ -45,10 +45,10 @@ class ItemConf : public Messager { using Index_ItemMap = std::unordered_map; // Finds the index (Type) to value (Index_ItemVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemMap& FindItemMap() const; - // Finds a vector of all values of the given key. + const Index_ItemMap& FindItem() const; + // Finds a vector of all values of the given key(s). const Index_ItemVector* FindItem(protoconf::FruitType type) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstItem(protoconf::FruitType type) const; private: @@ -60,10 +60,10 @@ class ItemConf : public Messager { using Index_ItemInfoMap = std::unordered_map; // Finds the index (Param@ItemInfo) to value (Index_ItemInfoVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemInfoMap& FindItemInfoMap() const; - // Finds a vector of all values of the given key. + const Index_ItemInfoMap& FindItemInfo() const; + // Finds a vector of all values of the given key(s). const Index_ItemInfoVector* FindItemInfo(int32_t param) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstItemInfo(int32_t param) const; private: @@ -75,10 +75,10 @@ class ItemConf : public Messager { using Index_ItemDefaultInfoMap = std::unordered_map; // Finds the index (Default@ItemDefaultInfo) to value (Index_ItemDefaultInfoVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemDefaultInfoMap& FindItemDefaultInfoMap() const; - // Finds a vector of all values of the given key. + const Index_ItemDefaultInfoMap& FindItemDefaultInfo() const; + // Finds a vector of all values of the given key(s). const Index_ItemDefaultInfoVector* FindItemDefaultInfo(const std::string& default_) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstItemDefaultInfo(const std::string& default_) const; private: @@ -90,10 +90,10 @@ class ItemConf : public Messager { using Index_ItemExtInfoMap = std::unordered_map; // Finds the index (ExtType@ItemExtInfo) to value (Index_ItemExtInfoVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemExtInfoMap& FindItemExtInfoMap() const; - // Finds a vector of all values of the given key. + const Index_ItemExtInfoMap& FindItemExtInfo() const; + // Finds a vector of all values of the given key(s). const Index_ItemExtInfoVector* FindItemExtInfo(protoconf::FruitType ext_type) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstItemExtInfo(protoconf::FruitType ext_type) const; private: @@ -104,9 +104,13 @@ class ItemConf : public Messager { struct Index_AwardItemKey { uint32_t id; std::string name; +#if __cplusplus >= 202002L + bool operator==(const Index_AwardItemKey& other) const = default; +#else bool operator==(const Index_AwardItemKey& other) const { - return id == other.id && name == other.name; + return std::tie(id, name) == std::tie(other.id, other.name); } +#endif }; struct Index_AwardItemKeyHasher { std::size_t operator()(const Index_AwardItemKey& key) const { @@ -117,10 +121,10 @@ class ItemConf : public Messager { using Index_AwardItemMap = std::unordered_map; // Finds the index ((ID,Name)@AwardItem) to value (Index_AwardItemVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_AwardItemMap& FindAwardItemMap() const; - // Finds a vector of all values of the given keys. + const Index_AwardItemMap& FindAwardItem() const; + // Finds a vector of all values of the given key(s). const Index_AwardItemVector* FindAwardItem(uint32_t id, const std::string& name) const; - // Finds the first value of the given keys. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstAwardItem(uint32_t id, const std::string& name) const; private: @@ -133,9 +137,13 @@ class ItemConf : public Messager { protoconf::FruitType type; int32_t param; protoconf::FruitType ext_type; +#if __cplusplus >= 202002L + bool operator==(const Index_SpecialItemKey& other) const = default; +#else bool operator==(const Index_SpecialItemKey& other) const { - return id == other.id && type == other.type && param == other.param && ext_type == other.ext_type; + return std::tie(id, type, param, ext_type) == std::tie(other.id, other.type, other.param, other.ext_type); } +#endif }; struct Index_SpecialItemKeyHasher { std::size_t operator()(const Index_SpecialItemKey& key) const { @@ -146,10 +154,10 @@ class ItemConf : public Messager { using Index_SpecialItemMap = std::unordered_map; // Finds the index ((ID,Type,Param,ExtType)@SpecialItem) to value (Index_SpecialItemVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_SpecialItemMap& FindSpecialItemMap() const; - // Finds a vector of all values of the given keys. + const Index_SpecialItemMap& FindSpecialItem() const; + // Finds a vector of all values of the given key(s). const Index_SpecialItemVector* FindSpecialItem(uint32_t id, protoconf::FruitType type, int32_t param, protoconf::FruitType ext_type) const; - // Finds the first value of the given keys. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstSpecialItem(uint32_t id, protoconf::FruitType type, int32_t param, protoconf::FruitType ext_type) const; private: @@ -161,10 +169,10 @@ class ItemConf : public Messager { using Index_ItemPathDirMap = std::unordered_map; // Finds the index (PathDir@ItemPathDir) to value (Index_ItemPathDirVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemPathDirMap& FindItemPathDirMap() const; - // Finds a vector of all values of the given key. + const Index_ItemPathDirMap& FindItemPathDir() const; + // Finds a vector of all values of the given key(s). const Index_ItemPathDirVector* FindItemPathDir(const std::string& dir) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstItemPathDir(const std::string& dir) const; private: @@ -176,10 +184,10 @@ class ItemConf : public Messager { using Index_ItemPathNameMap = std::unordered_map; // Finds the index (PathName@ItemPathName) to value (Index_ItemPathNameVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemPathNameMap& FindItemPathNameMap() const; - // Finds a vector of all values of the given key. + const Index_ItemPathNameMap& FindItemPathName() const; + // Finds a vector of all values of the given key(s). const Index_ItemPathNameVector* FindItemPathName(const std::string& name) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstItemPathName(const std::string& name) const; private: @@ -191,10 +199,10 @@ class ItemConf : public Messager { using Index_ItemPathFriendIDMap = std::unordered_map; // Finds the index (PathFriendID@ItemPathFriendID) to value (Index_ItemPathFriendIDVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ItemPathFriendIDMap& FindItemPathFriendIDMap() const; - // Finds a vector of all values of the given key. + const Index_ItemPathFriendIDMap& FindItemPathFriendID() const; + // Finds a vector of all values of the given key(s). const Index_ItemPathFriendIDVector* FindItemPathFriendID(uint32_t id) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstItemPathFriendID(uint32_t id) const; private: @@ -206,15 +214,57 @@ class ItemConf : public Messager { using Index_UseEffectTypeMap = std::unordered_map; // Finds the index (UseEffectType@UseEffectType) to value (Index_UseEffectTypeVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_UseEffectTypeMap& FindUseEffectTypeMap() const; - // Finds a vector of all values of the given key. + const Index_UseEffectTypeMap& FindUseEffectType() const; + // Finds a vector of all values of the given key(s). const Index_UseEffectTypeVector* FindUseEffectType(protoconf::UseEffect::Type type) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ItemConf::Item* FindFirstUseEffectType(protoconf::UseEffect::Type type) const; private: Index_UseEffectTypeMap index_use_effect_type_map_; + // OrderedIndex accessers. + // OrderedIndex: ExtType@ExtType + public: + using OrderedIndex_ExtTypeVector = std::vector; + using OrderedIndex_ExtTypeMap = std::map; + // Finds the ordered index (ExtType@ExtType) to value (OrderedIndex_ExtTypeVector) map. + // One key may correspond to multiple values, which are contained by a vector. + const OrderedIndex_ExtTypeMap& FindExtType() const; + // Finds a vector of all values of the given key(s). + const OrderedIndex_ExtTypeVector* FindExtType(protoconf::FruitType ext_type) const; + // Finds the first value of the given key(s). + const protoconf::ItemConf::Item* FindFirstExtType(protoconf::FruitType ext_type) const; + + private: + OrderedIndex_ExtTypeMap ordered_index_ext_type_map_; + + // OrderedIndex: (Param,ExtType)@ParamExtType + public: + struct OrderedIndex_ParamExtTypeKey { + int32_t param; + protoconf::FruitType ext_type; +#if __cplusplus >= 202002L + auto operator<=>(const OrderedIndex_ParamExtTypeKey& other) const = default; +#else + bool operator<(const OrderedIndex_ParamExtTypeKey& other) const { + return std::tie(param, ext_type) < std::tie(other.param, other.ext_type); + } +#endif + }; + using OrderedIndex_ParamExtTypeVector = std::vector; + using OrderedIndex_ParamExtTypeMap = std::map; + // Finds the ordered index ((Param,ExtType)@ParamExtType) to value (OrderedIndex_ParamExtTypeVector) map. + // One key may correspond to multiple values, which are contained by a vector. + const OrderedIndex_ParamExtTypeMap& FindParamExtType() const; + // Finds a vector of all values of the given key(s). + const OrderedIndex_ParamExtTypeVector* FindParamExtType(int32_t param, protoconf::FruitType ext_type) const; + // Finds the first value of the given key(s). + const protoconf::ItemConf::Item* FindFirstParamExtType(int32_t param, protoconf::FruitType ext_type) const; + + private: + OrderedIndex_ParamExtTypeMap ordered_index_param_ext_type_map_; + }; } // namespace tableau 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 86bb5765..1018fd65 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc @@ -24,13 +24,13 @@ bool ActivityConf::ProcessAfterLoad() { // OrderedMap init. ordered_map_.clear(); for (auto&& item1 : data_.activity_map()) { - ordered_map_[item1.first] = Activity_OrderedMapValue(Activity_Chapter_OrderedMap(), &item1.second); + ordered_map_[item1.first] = OrderedMap_ActivityValue(OrderedMap_Activity_ChapterMap(), &item1.second); auto&& ordered_map1 = ordered_map_[item1.first].first; for (auto&& item2 : item1.second.chapter_map()) { - ordered_map1[item2.first] = Activity_Chapter_OrderedMapValue(protoconf_Section_OrderedMap(), &item2.second); + ordered_map1[item2.first] = OrderedMap_Activity_ChapterValue(OrderedMap_protoconf_SectionMap(), &item2.second); auto&& ordered_map2 = ordered_map1[item2.first].first; for (auto&& item3 : item2.second.section_map()) { - ordered_map2[item3.first] = protoconf_Section_OrderedMapValue(int32_OrderedMap(), &item3.second); + ordered_map2[item3.first] = OrderedMap_protoconf_SectionValue(OrderedMap_int32Map(), &item3.second); auto&& ordered_map3 = ordered_map2[item3.first].first; for (auto&& item4 : item3.second.section_rank_map()) { ordered_map3[item4.first] = item4.second; @@ -121,11 +121,11 @@ const int32_t* ActivityConf::Get(uint64_t activity_id, uint32_t chapter_id, uint return &iter->second; } -const ActivityConf::Activity_OrderedMap* ActivityConf::GetOrderedMap() const { +const ActivityConf::OrderedMap_ActivityMap* ActivityConf::GetOrderedMap() const { return &ordered_map_; } -const ActivityConf::Activity_Chapter_OrderedMap* ActivityConf::GetOrderedMap(uint64_t activity_id) const { +const ActivityConf::OrderedMap_Activity_ChapterMap* ActivityConf::GetOrderedMap(uint64_t activity_id) const { const auto* conf = GetOrderedMap(); if (conf == nullptr) { return nullptr; @@ -137,7 +137,7 @@ const ActivityConf::Activity_Chapter_OrderedMap* ActivityConf::GetOrderedMap(uin return &iter->second.first; } -const ActivityConf::protoconf_Section_OrderedMap* ActivityConf::GetOrderedMap(uint64_t activity_id, uint32_t chapter_id) const { +const ActivityConf::OrderedMap_protoconf_SectionMap* ActivityConf::GetOrderedMap(uint64_t activity_id, uint32_t chapter_id) const { const auto* conf = GetOrderedMap(activity_id); if (conf == nullptr) { return nullptr; @@ -149,7 +149,7 @@ const ActivityConf::protoconf_Section_OrderedMap* ActivityConf::GetOrderedMap(ui return &iter->second.first; } -const ActivityConf::int32_OrderedMap* ActivityConf::GetOrderedMap(uint64_t activity_id, uint32_t chapter_id, uint32_t section_id) const { +const ActivityConf::OrderedMap_int32Map* ActivityConf::GetOrderedMap(uint64_t activity_id, uint32_t chapter_id, uint32_t section_id) const { const auto* conf = GetOrderedMap(activity_id, chapter_id); if (conf == nullptr) { return nullptr; @@ -162,7 +162,7 @@ const ActivityConf::int32_OrderedMap* ActivityConf::GetOrderedMap(uint64_t activ } // Index: ActivityName -const ActivityConf::Index_ActivityMap& ActivityConf::FindActivityMap() const { return index_activity_map_ ;} +const ActivityConf::Index_ActivityMap& ActivityConf::FindActivity() const { return index_activity_map_ ;} const ActivityConf::Index_ActivityVector* ActivityConf::FindActivity(const std::string& activity_name) const { auto iter = index_activity_map_.find(activity_name); @@ -181,7 +181,7 @@ const protoconf::ActivityConf::Activity* ActivityConf::FindFirstActivity(const s } // Index: ChapterID -const ActivityConf::Index_ChapterMap& ActivityConf::FindChapterMap() const { return index_chapter_map_ ;} +const ActivityConf::Index_ChapterMap& ActivityConf::FindChapter() const { return index_chapter_map_ ;} const ActivityConf::Index_ChapterVector* ActivityConf::FindChapter(uint32_t chapter_id) const { auto iter = index_chapter_map_.find(chapter_id); @@ -200,7 +200,7 @@ const protoconf::ActivityConf::Activity::Chapter* ActivityConf::FindFirstChapter } // Index: ChapterName@NamedChapter -const ActivityConf::Index_NamedChapterMap& ActivityConf::FindNamedChapterMap() const { return index_named_chapter_map_ ;} +const ActivityConf::Index_NamedChapterMap& ActivityConf::FindNamedChapter() const { return index_named_chapter_map_ ;} const ActivityConf::Index_NamedChapterVector* ActivityConf::FindNamedChapter(const std::string& chapter_name) const { auto iter = index_named_chapter_map_.find(chapter_name); @@ -219,7 +219,7 @@ const protoconf::ActivityConf::Activity::Chapter* ActivityConf::FindFirstNamedCh } // Index: SectionItemID@Award -const ActivityConf::Index_AwardMap& ActivityConf::FindAwardMap() const { return index_award_map_ ;} +const ActivityConf::Index_AwardMap& ActivityConf::FindAward() const { return index_award_map_ ;} const ActivityConf::Index_AwardVector* ActivityConf::FindAward(uint32_t id) const { auto iter = index_award_map_.find(id); @@ -237,7 +237,6 @@ const protoconf::Section::SectionItem* ActivityConf::FindFirstAward(uint32_t id) return conf->front(); } - const std::string ChapterConf::kProtoName = protoconf::ChapterConf::GetDescriptor()->name(); bool ChapterConf::Load(const std::filesystem::path& dir, Format fmt, std::shared_ptr options /* = nullptr */) { @@ -319,6 +318,7 @@ bool TaskConf::ProcessAfterLoad() { ordered_index_ordered_task_map_.clear(); ordered_index_task_expiry_map_.clear(); ordered_index_sorted_task_expiry_map_.clear(); + ordered_index_activity_expiry_map_.clear(); for (auto&& item1 : data_.task_map()) { { // OrderedIndex: Goal@OrderedTask @@ -332,6 +332,11 @@ bool TaskConf::ProcessAfterLoad() { // OrderedIndex: Expiry@SortedTaskExpiry ordered_index_sorted_task_expiry_map_[item1.second.expiry().seconds()].push_back(&item1.second); } + { + // OrderedIndex: (Expiry,ActivityID)@ActivityExpiry + OrderedIndex_ActivityExpiryKey key{item1.second.expiry().seconds(), item1.second.activity_id()}; + ordered_index_activity_expiry_map_[key].push_back(&item1.second); + } } // OrderedIndex(sort): Goal@OrderedTask for (auto&& item : ordered_index_ordered_task_map_) { @@ -362,7 +367,7 @@ const protoconf::TaskConf::Task* TaskConf::Get(int64_t id) const { } // Index: ActivityID -const TaskConf::Index_TaskMap& TaskConf::FindTaskMap() const { return index_task_map_ ;} +const TaskConf::Index_TaskMap& TaskConf::FindTask() const { return index_task_map_ ;} const TaskConf::Index_TaskVector* TaskConf::FindTask(int64_t activity_id) const { auto iter = index_task_map_.find(activity_id); @@ -380,7 +385,6 @@ const protoconf::TaskConf::Task* TaskConf::FindFirstTask(int64_t activity_id) co return conf->front(); } - // OrderedIndex: Goal@OrderedTask const TaskConf::OrderedIndex_OrderedTaskMap& TaskConf::FindOrderedTask() const { return ordered_index_ordered_task_map_ ;} @@ -438,5 +442,23 @@ const protoconf::TaskConf::Task* TaskConf::FindFirstSortedTaskExpiry(int64_t exp return conf->front(); } +// OrderedIndex: (Expiry,ActivityID)@ActivityExpiry +const TaskConf::OrderedIndex_ActivityExpiryMap& TaskConf::FindActivityExpiry() const { return ordered_index_activity_expiry_map_ ;} + +const TaskConf::OrderedIndex_ActivityExpiryVector* TaskConf::FindActivityExpiry(int64_t expiry, int64_t activity_id) const { + auto iter = ordered_index_activity_expiry_map_.find({expiry, activity_id}); + if (iter == ordered_index_activity_expiry_map_.end()) { + return nullptr; + } + return &iter->second; +} + +const protoconf::TaskConf::Task* TaskConf::FindFirstActivityExpiry(int64_t expiry, int64_t activity_id) const { + auto conf = FindActivityExpiry(expiry, activity_id); + if (conf == nullptr || conf->empty()) { + return nullptr; + } + return conf->front(); +} } // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h index b3d6df19..7a9f2273 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h @@ -35,23 +35,23 @@ class ActivityConf : public Messager { // OrderedMap accessers. public: - using int32_OrderedMap = std::map; - const int32_OrderedMap* GetOrderedMap(uint64_t activity_id, uint32_t chapter_id, uint32_t section_id) const; + using OrderedMap_int32Map = std::map; + const OrderedMap_int32Map* GetOrderedMap(uint64_t activity_id, uint32_t chapter_id, uint32_t section_id) const; - using protoconf_Section_OrderedMapValue = std::pair; - using protoconf_Section_OrderedMap = std::map; - const protoconf_Section_OrderedMap* GetOrderedMap(uint64_t activity_id, uint32_t chapter_id) const; + using OrderedMap_protoconf_SectionValue = std::pair; + using OrderedMap_protoconf_SectionMap = std::map; + const OrderedMap_protoconf_SectionMap* GetOrderedMap(uint64_t activity_id, uint32_t chapter_id) const; - using Activity_Chapter_OrderedMapValue = std::pair; - using Activity_Chapter_OrderedMap = std::map; - const Activity_Chapter_OrderedMap* GetOrderedMap(uint64_t activity_id) const; + using OrderedMap_Activity_ChapterValue = std::pair; + using OrderedMap_Activity_ChapterMap = std::map; + const OrderedMap_Activity_ChapterMap* GetOrderedMap(uint64_t activity_id) const; - using Activity_OrderedMapValue = std::pair; - using Activity_OrderedMap = std::map; - const Activity_OrderedMap* GetOrderedMap() const; + using OrderedMap_ActivityValue = std::pair; + using OrderedMap_ActivityMap = std::map; + const OrderedMap_ActivityMap* GetOrderedMap() const; private: - Activity_OrderedMap ordered_map_; + OrderedMap_ActivityMap ordered_map_; // Index accessers. // Index: ActivityName @@ -60,10 +60,10 @@ class ActivityConf : public Messager { using Index_ActivityMap = std::unordered_map; // Finds the index (ActivityName) to value (Index_ActivityVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ActivityMap& FindActivityMap() const; - // Finds a vector of all values of the given key. + const Index_ActivityMap& FindActivity() const; + // Finds a vector of all values of the given key(s). const Index_ActivityVector* FindActivity(const std::string& activity_name) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ActivityConf::Activity* FindFirstActivity(const std::string& activity_name) const; private: @@ -75,10 +75,10 @@ class ActivityConf : public Messager { using Index_ChapterMap = std::unordered_map; // Finds the index (ChapterID) to value (Index_ChapterVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_ChapterMap& FindChapterMap() const; - // Finds a vector of all values of the given key. + const Index_ChapterMap& FindChapter() const; + // Finds a vector of all values of the given key(s). const Index_ChapterVector* FindChapter(uint32_t chapter_id) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ActivityConf::Activity::Chapter* FindFirstChapter(uint32_t chapter_id) const; private: @@ -90,10 +90,10 @@ class ActivityConf : public Messager { using Index_NamedChapterMap = std::unordered_map; // Finds the index (ChapterName@NamedChapter) to value (Index_NamedChapterVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_NamedChapterMap& FindNamedChapterMap() const; - // Finds a vector of all values of the given key. + const Index_NamedChapterMap& FindNamedChapter() const; + // Finds a vector of all values of the given key(s). const Index_NamedChapterVector* FindNamedChapter(const std::string& chapter_name) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::ActivityConf::Activity::Chapter* FindFirstNamedChapter(const std::string& chapter_name) const; private: @@ -105,10 +105,10 @@ class ActivityConf : public Messager { using Index_AwardMap = std::unordered_map; // Finds the index (SectionItemID@Award) to value (Index_AwardVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_AwardMap& FindAwardMap() const; - // Finds a vector of all values of the given key. + const Index_AwardMap& FindAward() const; + // Finds a vector of all values of the given key(s). const Index_AwardVector* FindAward(uint32_t id) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::Section::SectionItem* FindFirstAward(uint32_t id) const; private: @@ -171,10 +171,10 @@ class TaskConf : public Messager { using Index_TaskMap = std::unordered_map; // Finds the index (ActivityID) to value (Index_TaskVector) hash map. // One key may correspond to multiple values, which are contained by a vector. - const Index_TaskMap& FindTaskMap() const; - // Finds a vector of all values of the given key. + const Index_TaskMap& FindTask() const; + // Finds a vector of all values of the given key(s). const Index_TaskVector* FindTask(int64_t activity_id) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::TaskConf::Task* FindFirstTask(int64_t activity_id) const; private: @@ -185,12 +185,12 @@ class TaskConf : public Messager { public: using OrderedIndex_OrderedTaskVector = std::vector; using OrderedIndex_OrderedTaskMap = std::map; - // Finds the ordered index (Goal@OrderedTask) to value (OrderedIndex_OrderedTaskVector) hash map. + // Finds the ordered index (Goal@OrderedTask) to value (OrderedIndex_OrderedTaskVector) map. // One key may correspond to multiple values, which are contained by a vector. const OrderedIndex_OrderedTaskMap& FindOrderedTask() const; - // Finds a vector of all values of the given key. + // Finds a vector of all values of the given key(s). const OrderedIndex_OrderedTaskVector* FindOrderedTask(int64_t goal) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::TaskConf::Task* FindFirstOrderedTask(int64_t goal) const; private: @@ -200,12 +200,12 @@ class TaskConf : public Messager { public: using OrderedIndex_TaskExpiryVector = std::vector; using OrderedIndex_TaskExpiryMap = std::map; - // Finds the ordered index (Expiry@TaskExpiry) to value (OrderedIndex_TaskExpiryVector) hash map. + // Finds the ordered index (Expiry@TaskExpiry) to value (OrderedIndex_TaskExpiryVector) map. // One key may correspond to multiple values, which are contained by a vector. const OrderedIndex_TaskExpiryMap& FindTaskExpiry() const; - // Finds a vector of all values of the given key. + // Finds a vector of all values of the given key(s). const OrderedIndex_TaskExpiryVector* FindTaskExpiry(int64_t expiry) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::TaskConf::Task* FindFirstTaskExpiry(int64_t expiry) const; private: @@ -215,17 +215,43 @@ class TaskConf : public Messager { public: using OrderedIndex_SortedTaskExpiryVector = std::vector; using OrderedIndex_SortedTaskExpiryMap = std::map; - // Finds the ordered index (Expiry@SortedTaskExpiry) to value (OrderedIndex_SortedTaskExpiryVector) hash map. + // Finds the ordered index (Expiry@SortedTaskExpiry) to value (OrderedIndex_SortedTaskExpiryVector) map. // One key may correspond to multiple values, which are contained by a vector. const OrderedIndex_SortedTaskExpiryMap& FindSortedTaskExpiry() const; - // Finds a vector of all values of the given key. + // Finds a vector of all values of the given key(s). const OrderedIndex_SortedTaskExpiryVector* FindSortedTaskExpiry(int64_t expiry) const; - // Finds the first value of the given key. + // Finds the first value of the given key(s). const protoconf::TaskConf::Task* FindFirstSortedTaskExpiry(int64_t expiry) const; private: OrderedIndex_SortedTaskExpiryMap ordered_index_sorted_task_expiry_map_; + // OrderedIndex: (Expiry,ActivityID)@ActivityExpiry + public: + struct OrderedIndex_ActivityExpiryKey { + int64_t expiry; + int64_t activity_id; +#if __cplusplus >= 202002L + auto operator<=>(const OrderedIndex_ActivityExpiryKey& other) const = default; +#else + bool operator<(const OrderedIndex_ActivityExpiryKey& other) const { + return std::tie(expiry, activity_id) < std::tie(other.expiry, other.activity_id); + } +#endif + }; + using OrderedIndex_ActivityExpiryVector = std::vector; + using OrderedIndex_ActivityExpiryMap = std::map; + // Finds the ordered index ((Expiry,ActivityID)@ActivityExpiry) to value (OrderedIndex_ActivityExpiryVector) map. + // One key may correspond to multiple values, which are contained by a vector. + const OrderedIndex_ActivityExpiryMap& FindActivityExpiry() const; + // Finds a vector of all values of the given key(s). + const OrderedIndex_ActivityExpiryVector* FindActivityExpiry(int64_t expiry, int64_t activity_id) const; + // Finds the first value of the given key(s). + const protoconf::TaskConf::Task* FindFirstActivityExpiry(int64_t expiry, int64_t activity_id) const; + + private: + OrderedIndex_ActivityExpiryMap ordered_index_activity_expiry_map_; + }; } // namespace tableau 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 4f5918c5..b4a6bcf7 100644 --- a/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/hero_conf.pc.go @@ -159,10 +159,10 @@ func (x *HeroConf) FindFirstAttr(title string) *protoconf.HeroConf_Hero_Attr { } // OrderedMap types. -type BaseHeroItemMap_OrderedMap = treemap.TreeMap[string, *base.Item] +type HeroBaseConf_OrderedMap_base_ItemMap = treemap.TreeMap[string, *base.Item] -type ProtoconfHeroBaseConfHeroMap_OrderedMapValue = pair.Pair[*BaseHeroItemMap_OrderedMap, *base.Hero] -type ProtoconfHeroBaseConfHeroMap_OrderedMap = treemap.TreeMap[string, *ProtoconfHeroBaseConfHeroMap_OrderedMapValue] +type HeroBaseConf_OrderedMap_base_HeroValue = pair.Pair[*HeroBaseConf_OrderedMap_base_ItemMap, *base.Hero] +type HeroBaseConf_OrderedMap_base_HeroMap = treemap.TreeMap[string, *HeroBaseConf_OrderedMap_base_HeroValue] // HeroBaseConf is a wrapper around protobuf message: protoconf.HeroBaseConf. // @@ -174,7 +174,7 @@ type ProtoconfHeroBaseConfHeroMap_OrderedMap = treemap.TreeMap[string, *Protocon type HeroBaseConf struct { UnimplementedMessager data, originalData *protoconf.HeroBaseConf - orderedMap *ProtoconfHeroBaseConfHeroMap_OrderedMap + orderedMap *HeroBaseConf_OrderedMap_base_HeroMap } // Name returns the HeroBaseConf's message name. @@ -237,10 +237,10 @@ func (x *HeroBaseConf) originalMessage() proto.Message { // processAfterLoad runs after this messager is loaded. func (x *HeroBaseConf) processAfterLoad() error { // OrderedMap init. - x.orderedMap = treemap.New[string, *ProtoconfHeroBaseConfHeroMap_OrderedMapValue]() + x.orderedMap = treemap.New[string, *HeroBaseConf_OrderedMap_base_HeroValue]() for k1, v1 := range x.Data().GetHeroMap() { map1 := x.orderedMap - k1v := &ProtoconfHeroBaseConfHeroMap_OrderedMapValue{ + k1v := &HeroBaseConf_OrderedMap_base_HeroValue{ First: treemap.New[string, *base.Item](), Second: v1, } @@ -280,13 +280,13 @@ func (x *HeroBaseConf) Get2(name string, id string) (*base.Item, error) { } // GetOrderedMap returns the 1-level ordered map. -func (x *HeroBaseConf) GetOrderedMap() *ProtoconfHeroBaseConfHeroMap_OrderedMap { +func (x *HeroBaseConf) GetOrderedMap() *HeroBaseConf_OrderedMap_base_HeroMap { return x.orderedMap } // GetOrderedMap1 finds value in the 1-level ordered map. It will return // NotFound error if the key is not found. -func (x *HeroBaseConf) GetOrderedMap1(name string) (*BaseHeroItemMap_OrderedMap, error) { +func (x *HeroBaseConf) GetOrderedMap1(name string) (*HeroBaseConf_OrderedMap_base_ItemMap, error) { conf := x.orderedMap if val, ok := conf.Get(name); !ok { return nil, fmt.Errorf("name(%v) %w", name, ErrNotFound) 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 1cecc5e9..ffca0605 100644 --- a/test/go-tableau-loader/protoconf/loader/item_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/item_conf.pc.go @@ -19,7 +19,7 @@ import ( ) // OrderedMap types. -type ProtoconfItemConfItemMap_OrderedMap = treemap.TreeMap[uint32, *protoconf.ItemConf_Item] +type ItemConf_OrderedMap_ItemMap = treemap.TreeMap[uint32, *protoconf.ItemConf_Item] // Index types. // Index: Type @@ -62,6 +62,25 @@ type ItemConf_Index_ItemPathFriendIDMap = map[uint32][]*protoconf.ItemConf_Item // Index: UseEffectType@UseEffectType type ItemConf_Index_UseEffectTypeMap = map[protoconf.UseEffect_Type][]*protoconf.ItemConf_Item +// OrderedIndex types. +// OrderedIndex: ExtType@ExtType +type ItemConf_OrderedIndex_ExtTypeMap = treemap.TreeMap[protoconf.FruitType, []*protoconf.ItemConf_Item] + +// OrderedIndex: (Param,ExtType)@ParamExtType +type ItemConf_OrderedIndex_ParamExtTypeKey struct { + Param int32 + ExtType protoconf.FruitType +} + +func (x ItemConf_OrderedIndex_ParamExtTypeKey) Less(other ItemConf_OrderedIndex_ParamExtTypeKey) bool { + if x.Param != other.Param { + return x.Param < other.Param + } + return x.ExtType < other.ExtType +} + +type ItemConf_OrderedIndex_ParamExtTypeMap = treemap.TreeMap[ItemConf_OrderedIndex_ParamExtTypeKey, []*protoconf.ItemConf_Item] + // ItemConf is a wrapper around protobuf message: protoconf.ItemConf. // // It is designed for three goals: @@ -71,18 +90,20 @@ type ItemConf_Index_UseEffectTypeMap = map[protoconf.UseEffect_Type][]*protoconf // 3. Extensibility: Map, OrdererdMap, Index, OrderedIndex... type ItemConf struct { UnimplementedMessager - data, originalData *protoconf.ItemConf - orderedMap *ProtoconfItemConfItemMap_OrderedMap - indexItemMap ItemConf_Index_ItemMap - indexItemInfoMap ItemConf_Index_ItemInfoMap - indexItemDefaultInfoMap ItemConf_Index_ItemDefaultInfoMap - indexItemExtInfoMap ItemConf_Index_ItemExtInfoMap - indexAwardItemMap ItemConf_Index_AwardItemMap - indexSpecialItemMap ItemConf_Index_SpecialItemMap - indexItemPathDirMap ItemConf_Index_ItemPathDirMap - indexItemPathNameMap ItemConf_Index_ItemPathNameMap - indexItemPathFriendIdMap ItemConf_Index_ItemPathFriendIDMap - indexUseEffectTypeMap ItemConf_Index_UseEffectTypeMap + data, originalData *protoconf.ItemConf + orderedMap *ItemConf_OrderedMap_ItemMap + indexItemMap ItemConf_Index_ItemMap + indexItemInfoMap ItemConf_Index_ItemInfoMap + indexItemDefaultInfoMap ItemConf_Index_ItemDefaultInfoMap + indexItemExtInfoMap ItemConf_Index_ItemExtInfoMap + indexAwardItemMap ItemConf_Index_AwardItemMap + indexSpecialItemMap ItemConf_Index_SpecialItemMap + indexItemPathDirMap ItemConf_Index_ItemPathDirMap + indexItemPathNameMap ItemConf_Index_ItemPathNameMap + indexItemPathFriendIdMap ItemConf_Index_ItemPathFriendIDMap + indexUseEffectTypeMap ItemConf_Index_UseEffectTypeMap + orderedIndexExtTypeMap *ItemConf_OrderedIndex_ExtTypeMap + orderedIndexParamExtTypeMap *ItemConf_OrderedIndex_ParamExtTypeMap } // Name returns the ItemConf's message name. @@ -238,6 +259,36 @@ func (x *ItemConf) processAfterLoad() error { return item[i].GetUseEffect().GetType() < item[j].GetUseEffect().GetType() }) } + // OrderedIndex init. + x.orderedIndexExtTypeMap = treemap.New[protoconf.FruitType, []*protoconf.ItemConf_Item]() + x.orderedIndexParamExtTypeMap = treemap.New2[ItemConf_OrderedIndex_ParamExtTypeKey, []*protoconf.ItemConf_Item]() + for _, item1 := range x.data.GetItemMap() { + { + // OrderedIndex: ExtType@ExtType + for _, item2 := range item1.GetExtTypeList() { + key := item2 + value, _ := x.orderedIndexExtTypeMap.Get(key) + x.orderedIndexExtTypeMap.Put(key, append(value, item1)) + } + } + { + // OrderedIndex: (Param,ExtType)@ParamExtType + for _, indexItem0 := range item1.GetParamList() { + for _, indexItem1 := range item1.GetExtTypeList() { + key := ItemConf_OrderedIndex_ParamExtTypeKey{indexItem0, indexItem1} + value, _ := x.orderedIndexParamExtTypeMap.Get(key) + x.orderedIndexParamExtTypeMap.Put(key, append(value, item1)) + } + } + } + } + // OrderedIndex(sort): (Param,ExtType)@ParamExtType + x.orderedIndexParamExtTypeMap.Range(func(key ItemConf_OrderedIndex_ParamExtTypeKey, item []*protoconf.ItemConf_Item) bool { + sort.Slice(item, func(i, j int) bool { + return item[i].GetId() < item[j].GetId() + }) + return true + }) return nil } @@ -253,7 +304,7 @@ func (x *ItemConf) Get1(id uint32) (*protoconf.ItemConf_Item, error) { } // GetOrderedMap returns the 1-level ordered map. -func (x *ItemConf) GetOrderedMap() *ProtoconfItemConfItemMap_OrderedMap { +func (x *ItemConf) GetOrderedMap() *ItemConf_OrderedMap_ItemMap { return x.orderedMap } @@ -487,6 +538,54 @@ func (x *ItemConf) FindFirstUseEffectType(type_ protoconf.UseEffect_Type) *proto return nil } +// OrderedIndex: ExtType@ExtType + +// FindExtTypeMap finds the ordered index (ExtType@ExtType) to value (protoconf.ItemConf_Item) treemap. +// One key may correspond to multiple values, which are contained by a slice. +func (x *ItemConf) FindExtTypeMap() *ItemConf_OrderedIndex_ExtTypeMap { + return x.orderedIndexExtTypeMap +} + +// FindExtType finds a slice of all values of the given key. +func (x *ItemConf) FindExtType(extType protoconf.FruitType) []*protoconf.ItemConf_Item { + val, _ := x.orderedIndexExtTypeMap.Get(extType) + return val +} + +// FindFirstExtType finds the first value of the given key, +// or nil if no value found. +func (x *ItemConf) FindFirstExtType(extType protoconf.FruitType) *protoconf.ItemConf_Item { + val := x.FindExtType(extType) + if len(val) > 0 { + return val[0] + } + return nil +} + +// OrderedIndex: (Param,ExtType)@ParamExtType + +// FindParamExtTypeMap finds the ordered index ((Param,ExtType)@ParamExtType) to value (protoconf.ItemConf_Item) treemap. +// One key may correspond to multiple values, which are contained by a slice. +func (x *ItemConf) FindParamExtTypeMap() *ItemConf_OrderedIndex_ParamExtTypeMap { + return x.orderedIndexParamExtTypeMap +} + +// FindParamExtType finds a slice of all values of the given key. +func (x *ItemConf) FindParamExtType(param int32, extType protoconf.FruitType) []*protoconf.ItemConf_Item { + val, _ := x.orderedIndexParamExtTypeMap.Get(ItemConf_OrderedIndex_ParamExtTypeKey{param, extType}) + return val +} + +// FindFirstParamExtType finds the first value of the given key, +// or nil if no value found. +func (x *ItemConf) FindFirstParamExtType(param int32, extType protoconf.FruitType) *protoconf.ItemConf_Item { + val := x.FindParamExtType(param, extType) + if len(val) > 0 { + return val[0] + } + return nil +} + func init() { Register(func() Messager { return new(ItemConf) 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 c5ae05d9..67385585 100644 --- a/test/go-tableau-loader/protoconf/loader/test_conf.pc.go +++ b/test/go-tableau-loader/protoconf/loader/test_conf.pc.go @@ -20,16 +20,16 @@ import ( ) // OrderedMap types. -type ProtoconfSectionSectionRankMap_OrderedMap = treemap.TreeMap[uint32, int32] +type ActivityConf_OrderedMap_int32Map = treemap.TreeMap[uint32, int32] -type ProtoconfActivityConfActivityChapterSectionMap_OrderedMapValue = pair.Pair[*ProtoconfSectionSectionRankMap_OrderedMap, *protoconf.Section] -type ProtoconfActivityConfActivityChapterSectionMap_OrderedMap = treemap.TreeMap[uint32, *ProtoconfActivityConfActivityChapterSectionMap_OrderedMapValue] +type ActivityConf_OrderedMap_protoconf_SectionValue = pair.Pair[*ActivityConf_OrderedMap_int32Map, *protoconf.Section] +type ActivityConf_OrderedMap_protoconf_SectionMap = treemap.TreeMap[uint32, *ActivityConf_OrderedMap_protoconf_SectionValue] -type ProtoconfActivityConfActivityChapterMap_OrderedMapValue = pair.Pair[*ProtoconfActivityConfActivityChapterSectionMap_OrderedMap, *protoconf.ActivityConf_Activity_Chapter] -type ProtoconfActivityConfActivityChapterMap_OrderedMap = treemap.TreeMap[uint32, *ProtoconfActivityConfActivityChapterMap_OrderedMapValue] +type ActivityConf_OrderedMap_Activity_ChapterValue = pair.Pair[*ActivityConf_OrderedMap_protoconf_SectionMap, *protoconf.ActivityConf_Activity_Chapter] +type ActivityConf_OrderedMap_Activity_ChapterMap = treemap.TreeMap[uint32, *ActivityConf_OrderedMap_Activity_ChapterValue] -type ProtoconfActivityConfActivityMap_OrderedMapValue = pair.Pair[*ProtoconfActivityConfActivityChapterMap_OrderedMap, *protoconf.ActivityConf_Activity] -type ProtoconfActivityConfActivityMap_OrderedMap = treemap.TreeMap[uint64, *ProtoconfActivityConfActivityMap_OrderedMapValue] +type ActivityConf_OrderedMap_ActivityValue = pair.Pair[*ActivityConf_OrderedMap_Activity_ChapterMap, *protoconf.ActivityConf_Activity] +type ActivityConf_OrderedMap_ActivityMap = treemap.TreeMap[uint64, *ActivityConf_OrderedMap_ActivityValue] // Index types. // Index: ActivityName @@ -54,7 +54,7 @@ type ActivityConf_Index_AwardMap = map[uint32][]*protoconf.Section_SectionItem type ActivityConf struct { UnimplementedMessager data, originalData *protoconf.ActivityConf - orderedMap *ProtoconfActivityConfActivityMap_OrderedMap + orderedMap *ActivityConf_OrderedMap_ActivityMap indexActivityMap ActivityConf_Index_ActivityMap indexChapterMap ActivityConf_Index_ChapterMap indexNamedChapterMap ActivityConf_Index_NamedChapterMap @@ -121,24 +121,24 @@ func (x *ActivityConf) originalMessage() proto.Message { // processAfterLoad runs after this messager is loaded. func (x *ActivityConf) processAfterLoad() error { // OrderedMap init. - x.orderedMap = treemap.New[uint64, *ProtoconfActivityConfActivityMap_OrderedMapValue]() + x.orderedMap = treemap.New[uint64, *ActivityConf_OrderedMap_ActivityValue]() for k1, v1 := range x.Data().GetActivityMap() { map1 := x.orderedMap - k1v := &ProtoconfActivityConfActivityMap_OrderedMapValue{ - First: treemap.New[uint32, *ProtoconfActivityConfActivityChapterMap_OrderedMapValue](), + k1v := &ActivityConf_OrderedMap_ActivityValue{ + First: treemap.New[uint32, *ActivityConf_OrderedMap_Activity_ChapterValue](), Second: v1, } map1.Put(k1, k1v) for k2, v2 := range v1.GetChapterMap() { map2 := k1v.First - k2v := &ProtoconfActivityConfActivityChapterMap_OrderedMapValue{ - First: treemap.New[uint32, *ProtoconfActivityConfActivityChapterSectionMap_OrderedMapValue](), + k2v := &ActivityConf_OrderedMap_Activity_ChapterValue{ + First: treemap.New[uint32, *ActivityConf_OrderedMap_protoconf_SectionValue](), Second: v2, } map2.Put(k2, k2v) for k3, v3 := range v2.GetSectionMap() { map3 := k2v.First - k3v := &ProtoconfActivityConfActivityChapterSectionMap_OrderedMapValue{ + k3v := &ActivityConf_OrderedMap_protoconf_SectionValue{ First: treemap.New[uint32, int32](), Second: v3, } @@ -249,13 +249,13 @@ func (x *ActivityConf) Get4(activityId uint64, chapterId uint32, sectionId uint3 } // GetOrderedMap returns the 1-level ordered map. -func (x *ActivityConf) GetOrderedMap() *ProtoconfActivityConfActivityMap_OrderedMap { +func (x *ActivityConf) GetOrderedMap() *ActivityConf_OrderedMap_ActivityMap { return x.orderedMap } // GetOrderedMap1 finds value in the 1-level ordered map. It will return // NotFound error if the key is not found. -func (x *ActivityConf) GetOrderedMap1(activityId uint64) (*ProtoconfActivityConfActivityChapterMap_OrderedMap, error) { +func (x *ActivityConf) GetOrderedMap1(activityId uint64) (*ActivityConf_OrderedMap_Activity_ChapterMap, error) { conf := x.orderedMap if val, ok := conf.Get(activityId); !ok { return nil, fmt.Errorf("activityId(%v) %w", activityId, ErrNotFound) @@ -266,7 +266,7 @@ func (x *ActivityConf) GetOrderedMap1(activityId uint64) (*ProtoconfActivityConf // GetOrderedMap2 finds value in the 2-level ordered map. It will return // NotFound error if the key is not found. -func (x *ActivityConf) GetOrderedMap2(activityId uint64, chapterId uint32) (*ProtoconfActivityConfActivityChapterSectionMap_OrderedMap, error) { +func (x *ActivityConf) GetOrderedMap2(activityId uint64, chapterId uint32) (*ActivityConf_OrderedMap_protoconf_SectionMap, error) { conf, err := x.GetOrderedMap1(activityId) if err != nil { return nil, err @@ -280,7 +280,7 @@ func (x *ActivityConf) GetOrderedMap2(activityId uint64, chapterId uint32) (*Pro // GetOrderedMap3 finds value in the 3-level ordered map. It will return // NotFound error if the key is not found. -func (x *ActivityConf) GetOrderedMap3(activityId uint64, chapterId uint32, sectionId uint32) (*ProtoconfSectionSectionRankMap_OrderedMap, error) { +func (x *ActivityConf) GetOrderedMap3(activityId uint64, chapterId uint32, sectionId uint32) (*ActivityConf_OrderedMap_int32Map, error) { conf, err := x.GetOrderedMap2(activityId, chapterId) if err != nil { return nil, err @@ -573,6 +573,21 @@ type TaskConf_OrderedIndex_TaskExpiryMap = treemap.TreeMap[int64, []*protoconf.T // OrderedIndex: Expiry@SortedTaskExpiry type TaskConf_OrderedIndex_SortedTaskExpiryMap = treemap.TreeMap[int64, []*protoconf.TaskConf_Task] +// OrderedIndex: (Expiry,ActivityID)@ActivityExpiry +type TaskConf_OrderedIndex_ActivityExpiryKey struct { + Expiry int64 + ActivityId int64 +} + +func (x TaskConf_OrderedIndex_ActivityExpiryKey) Less(other TaskConf_OrderedIndex_ActivityExpiryKey) bool { + if x.Expiry != other.Expiry { + return x.Expiry < other.Expiry + } + return x.ActivityId < other.ActivityId +} + +type TaskConf_OrderedIndex_ActivityExpiryMap = treemap.TreeMap[TaskConf_OrderedIndex_ActivityExpiryKey, []*protoconf.TaskConf_Task] + // TaskConf is a wrapper around protobuf message: protoconf.TaskConf. // // It is designed for three goals: @@ -587,6 +602,7 @@ type TaskConf struct { orderedIndexOrderedTaskMap *TaskConf_OrderedIndex_OrderedTaskMap orderedIndexTaskExpiryMap *TaskConf_OrderedIndex_TaskExpiryMap orderedIndexSortedTaskExpiryMap *TaskConf_OrderedIndex_SortedTaskExpiryMap + orderedIndexActivityExpiryMap *TaskConf_OrderedIndex_ActivityExpiryMap } // Name returns the TaskConf's message name. @@ -670,6 +686,7 @@ func (x *TaskConf) processAfterLoad() error { x.orderedIndexOrderedTaskMap = treemap.New[int64, []*protoconf.TaskConf_Task]() 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 _, item1 := range x.data.GetTaskMap() { { // OrderedIndex: Goal@OrderedTask @@ -689,6 +706,12 @@ func (x *TaskConf) processAfterLoad() error { value, _ := x.orderedIndexSortedTaskExpiryMap.Get(key) x.orderedIndexSortedTaskExpiryMap.Put(key, append(value, item1)) } + { + // OrderedIndex: (Expiry,ActivityID)@ActivityExpiry + key := TaskConf_OrderedIndex_ActivityExpiryKey{item1.GetExpiry().GetSeconds(), item1.GetActivityId()} + value, _ := x.orderedIndexActivityExpiryMap.Get(key) + x.orderedIndexActivityExpiryMap.Put(key, append(value, item1)) + } } // OrderedIndex(sort): Goal@OrderedTask x.orderedIndexOrderedTaskMap.Range(func(key int64, item []*protoconf.TaskConf_Task) bool { @@ -816,6 +839,30 @@ func (x *TaskConf) FindFirstSortedTaskExpiry(expiry int64) *protoconf.TaskConf_T return nil } +// OrderedIndex: (Expiry,ActivityID)@ActivityExpiry + +// FindActivityExpiryMap finds the ordered index ((Expiry,ActivityID)@ActivityExpiry) to value (protoconf.TaskConf_Task) treemap. +// One key may correspond to multiple values, which are contained by a slice. +func (x *TaskConf) FindActivityExpiryMap() *TaskConf_OrderedIndex_ActivityExpiryMap { + return x.orderedIndexActivityExpiryMap +} + +// FindActivityExpiry finds a slice of all values of the given key. +func (x *TaskConf) FindActivityExpiry(expiry int64, activityId int64) []*protoconf.TaskConf_Task { + val, _ := x.orderedIndexActivityExpiryMap.Get(TaskConf_OrderedIndex_ActivityExpiryKey{expiry, activityId}) + return val +} + +// FindFirstActivityExpiry finds the first value of the given key, +// or nil if no value found. +func (x *TaskConf) FindFirstActivityExpiry(expiry int64, activityId int64) *protoconf.TaskConf_Task { + val := x.FindActivityExpiry(expiry, activityId) + if len(val) > 0 { + return val[0] + } + return nil +} + func init() { Register(func() Messager { return new(ActivityConf) diff --git a/test/proto/item_conf.proto b/test/proto/item_conf.proto index 218deb5f..7c143864 100644 --- a/test/proto/item_conf.proto +++ b/test/proto/item_conf.proto @@ -16,6 +16,8 @@ message ItemConf { option (tableau.worksheet) = { name: "ItemConf" ordered_map: true + ordered_index: "ExtType@ExtType" + ordered_index: "(Param,ExtType)@ParamExtType" index: "Type" index: "Param@ItemInfo" index: "Default@ItemDefaultInfo" // For testing programming language keyword conflicts @@ -53,7 +55,9 @@ message Path { } message UseEffect { - option (tableau.union) = {name: "UseEffect"}; + option (tableau.union) = { + name: "UseEffect" + }; Type type = 9999 [(tableau.field) = { name: "Type" }]; oneof value { diff --git a/test/proto/test_conf.proto b/test/proto/test_conf.proto index 2f86aa4c..977b639d 100644 --- a/test/proto/test_conf.proto +++ b/test/proto/test_conf.proto @@ -103,6 +103,7 @@ message TaskConf { ordered_index: "Goal@OrderedTask" ordered_index: "Expiry@TaskExpiry" ordered_index: "Expiry@SortedTaskExpiry" + ordered_index: "(Expiry,ActivityID)@ActivityExpiry" }; map task_map = 1 [(tableau.field) = { key: "ID" layout: LAYOUT_VERTICAL }];