├── .clang-format ├── .clangd ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── bench ├── binop.c ├── matmul.c └── run.sh ├── examples ├── model-from-mem │ ├── build.sh │ └── main.c ├── onnx_loader │ ├── Makefile │ └── onnx_loader.c ├── resnet-18 │ ├── Makefile │ ├── README.md │ ├── class-list.inc │ └── resnet-18.c └── simple │ ├── Makefile │ └── simple.c ├── mint.h ├── mint_onnx.h ├── onnx ├── README.md ├── onnx.proto3 ├── onnx.proto3.pb-c.c ├── onnx.proto3.pb-c.h ├── protobuf-c.c └── protobuf-c.h └── tests ├── run_test.sh └── tensor.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | AlignConsecutiveAssignments: "Consecutive" 3 | AlignConsecutiveDeclarations: "AcrossComments" 4 | AlignConsecutiveMacros: true 5 | IndentWidth: 4 6 | -------------------------------------------------------------------------------- /.clangd: -------------------------------------------------------------------------------- 1 | Diagnostics: 2 | Suppress: ["ODR"] 3 | 4 | If: 5 | PathMatch: [.*\.h, .*\.hpp] 6 | 7 | CompileFlags: 8 | Add: [-DMT_IMPLEMENTATION, -DMT_USE_NEON, -DMT_ONNX_IMPLEMENTATION] 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.dbg 2 | *.elf 3 | *.jpg 4 | .DS_Store 5 | alexnet 6 | alexnet_mt 7 | *.dSYM 8 | vendors 9 | *.dat 10 | *.mt 11 | *.onnx 12 | .venv 13 | *.out 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "vector": "c" 4 | } 5 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mint is a single-file header only library for tensor manipulation. It also 2 | enables importing and executing *some* of neural net models. Mint aims to be 3 | dependency-free (except for C standard lib) and easily distributed. However, 4 | it is possible to integrate with the other libraries such as BLAS if needed. 5 | 6 | Some of notable features: 7 | 8 | - NumPy style broadcasting 9 | - BLAS backend (optional) 10 | - ARM NEON SIMD acceleration (enable with `#define MT_USE_NEON`). 11 | - For Apple Silicon devices, you can additionally define `#define MT_USE_APPLE_ACCELERATE` to even more performance. Specify Accelerate framework during compilation. 12 | 13 | ## Issues 14 | 15 | - Got a `layer kind xxx is not supported yet` error? Try running `onnxsim`. It might trim those pesky unsupported layers 16 | - If onnxsim still gives you unsupported layers: just open an issue and I'll try to implement it when I can `¯\_(ツ)_/¯` 17 | 18 | 19 | ## Tested models 20 | 21 | ### Torchvision Models 22 | 23 | The torchvision models are dumped into ONNX, then converted to Mint model format for inference. 24 | 25 | - AlexNet 26 | - VGG-19 27 | - ResNet-18 28 | 29 | ### Fast neural style transfer 30 | 31 | All models [here](https://github.com/onnx/models/tree/main/validated/vision/style_transfer/fast_neural_style) with opset 8 work well 32 | -------------------------------------------------------------------------------- /bench/binop.c: -------------------------------------------------------------------------------- 1 | // clang-format off 2 | #define MT_IMPLEMENTATION 3 | #include "../mint.h" 4 | #include 5 | #include 6 | 7 | int main() { 8 | mt_tensor *t1 = mt_tensor_alloc_fill(MT_ARR_INT(100000, 10000), 2, 1.0f); 9 | mt_tensor *t2 = mt_tensor_alloc_fill(MT_ARR_INT(100000, 10000), 2, 1.0f); 10 | mt_tensor *t3 = mt_add(t1, t2); 11 | 12 | mt_tensor_free(t1); 13 | mt_tensor_free(t2); 14 | mt_tensor_free(t3); 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /bench/matmul.c: -------------------------------------------------------------------------------- 1 | // clang-format off 2 | #define MT_IMPLEMENTATION 3 | #include "../mint.h" 4 | #include 5 | #include 6 | 7 | int main() { 8 | mt_tensor *t1 = mt_tensor_alloc_fill(MT_ARR_INT(10000, 1000), 2, 1.0f); 9 | mt_tensor *t2 = mt_tensor_alloc_fill(MT_ARR_INT(1000, 10000), 2, 1.0f); 10 | mt_tensor *t3 = mt_matmul(t1, t2); 11 | 12 | mt_tensor_free(t1); 13 | mt_tensor_free(t2); 14 | mt_tensor_free(t3); 15 | 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /bench/run.sh: -------------------------------------------------------------------------------- 1 | # Regular C 2 | gcc -DMT_USE_PTHREAD -lpthread -O3 -o binop.out binop.c 3 | time ./binop.out 4 | 5 | # gcc -DMT_USE_PTHREAD -lpthread -O3 -o matmul.out matmul.c 6 | # time ./matmul.out 7 | 8 | # With NEON 9 | gcc -DMT_USE_PTHREAD -lpthread -DMT_USE_NEON -O3 -o binop_neon.out binop.c 10 | time ./binop_neon.out 11 | 12 | # With Accelerate framework 13 | gcc -DMT_USE_PTHREAD -lpthread -DMT_USE_NEON -DMT_USE_APPLE_ACCELERATE -O3 -framework Accelerate -o matmul_neon_accelerate.out matmul.c 14 | time ./matmul_neon_accelerate.out -------------------------------------------------------------------------------- /examples/model-from-mem/build.sh: -------------------------------------------------------------------------------- 1 | python ../helper/download-resnet.py 2 | python ../../scripts/dump.py resnet-18.onnx 3 | gcc -o main.out main.c 4 | -------------------------------------------------------------------------------- /examples/model-from-mem/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MT_IMPLEMENTATION 4 | #include "../../mint.h" 5 | 6 | int main() { 7 | 8 | FILE *fileptr = fopen("resnet-18.mt", "rb"); 9 | fseek(fileptr, 0, SEEK_END); 10 | long filelen = ftell(fileptr); 11 | rewind(fileptr); 12 | 13 | unsigned char *buffer = (unsigned char *)malloc(filelen * sizeof(char)); 14 | // Read in the entire model file 15 | fread(buffer, filelen, 1, fileptr); 16 | 17 | // Load model from the memory 18 | mt_model *model = mt_model_load_from_mem(buffer, filelen); 19 | 20 | // Do something with the model here 21 | // ... 22 | // ... 23 | 24 | // Cleanup 25 | mt_model_free(model); 26 | free(buffer); 27 | fclose(fileptr); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /examples/onnx_loader/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = onnx_loader 2 | SRC = $(TARGET).c ../../onnx/*.c 3 | CFLAGS = -g -Wall -Wextra -Werror 4 | 5 | .PHONY: all clean 6 | 7 | all: $(TARGET).out 8 | 9 | $(TARGET).out: $(SRC) ../../mint_onnx.h 10 | $(CC) $(CFLAGS) -o $@ $(SRC) $(LDFLAGS) 11 | 12 | clean: 13 | rm -f $(TARGET).out 14 | -------------------------------------------------------------------------------- /examples/onnx_loader/onnx_loader.c: -------------------------------------------------------------------------------- 1 | #define MT_IMPLEMENTATION 2 | #define MT_ONNX_IMPLEMENTATION 3 | #include "../../mint_onnx.h" 4 | #include 5 | 6 | int main(int argc, char **argv) { 7 | if (argc < 2) { 8 | printf("Usage: %s \n", argv[0]); 9 | return 1; 10 | } 11 | const char *filename = argv[1]; 12 | mt_model *model = mt_onnx_read_file(filename); 13 | if (model == NULL) { 14 | printf("Failed to load model\n"); 15 | return 1; 16 | } 17 | printf("Model %s loaded successfully\n", filename); 18 | 19 | mt_model_free(model); 20 | return 0; 21 | } -------------------------------------------------------------------------------- /examples/resnet-18/Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | CFLAGS = -Wall -Wextra -g -O3 -pedantic 3 | 4 | use_pthread ?= 0 5 | ifeq ($(use_pthread), 1) 6 | CFLAGS += -DMT_USE_PTHREAD -lpthread 7 | endif 8 | 9 | use_neon ?= 0 10 | ifeq ($(use_neon), 1) 11 | CFLAGS += -DMT_USE_NEON 12 | endif 13 | 14 | use_apple_accelerate ?= 0 15 | ifeq ($(use_apple_accelerate), 1) 16 | CFLAGS += -DMT_USE_APPLE_ACCELERATE -framework Accelerate 17 | endif 18 | 19 | TARGET = resnet-18 20 | SRC = $(TARGET).c 21 | LIB_SRC = ../../onnx/*.c 22 | 23 | .PHONY: all clean 24 | 25 | all: $(TARGET).out 26 | 27 | $(TARGET).out: $(SRC) $(LIB_SRC) ../../mint.h 28 | $(CC) $(CFLAGS) -o $@ $(SRC) $(LIB_SRC) $(LDFLAGS) 29 | 30 | clean: 31 | rm -f $(TARGET).out 32 | -------------------------------------------------------------------------------- /examples/resnet-18/README.md: -------------------------------------------------------------------------------- 1 | This example uses resnet from torchvision converted into onnx. 2 | 3 | ```python 4 | from torchvision import models 5 | import torch 6 | 7 | if __name__ == "__main__": 8 | model_path_in = "resnet-18.onnx" 9 | 10 | # Load the pre-trained ResNet-18 model 11 | resnet = models.resnet18(weights=models.ResNet18_Weights.DEFAULT) 12 | 13 | # Set the model to evaluation mode 14 | resnet.eval() 15 | 16 | # Create a dummy input tensor 17 | dummy_input = torch.randn(1, 3, 224, 224) 18 | 19 | # Export the model to ONNX 20 | torch.onnx.export( 21 | resnet, 22 | dummy_input, 23 | model_path_in, 24 | export_params=True, 25 | opset_version=12, 26 | do_constant_folding=True, 27 | input_names=["input"], 28 | output_names=["output"], 29 | dynamic_axes={ 30 | "input": {0: "batch_size"}, 31 | "output": {0: "batch_size"}, 32 | }, 33 | ) 34 | ``` -------------------------------------------------------------------------------- /examples/resnet-18/class-list.inc: -------------------------------------------------------------------------------- 1 | "tench, Tinca tinca", 2 | "goldfish, Carassius auratus", 3 | "great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias", 4 | "tiger shark, Galeocerdo cuvieri", 5 | "hammerhead, hammerhead shark", 6 | "electric ray, crampfish, numbfish, torpedo", 7 | "stingray", 8 | "cock", 9 | "hen", 10 | "ostrich, Struthio camelus", 11 | "brambling, Fringilla montifringilla", 12 | "goldfinch, Carduelis carduelis", 13 | "house finch, linnet, Carpodacus mexicanus", 14 | "junco, snowbird", 15 | "indigo bunting, indigo finch, indigo bird, Passerina cyanea", 16 | "robin, American robin, Turdus migratorius", 17 | "bulbul", 18 | "jay", 19 | "magpie", 20 | "chickadee", 21 | "water ouzel, dipper", 22 | "kite", 23 | "bald eagle, American eagle, Haliaeetus leucocephalus", 24 | "vulture", 25 | "great grey owl, great gray owl, Strix nebulosa", 26 | "European fire salamander, Salamandra salamandra", 27 | "common newt, Triturus vulgaris", 28 | "eft", 29 | "spotted salamander, Ambystoma maculatum", 30 | "axolotl, mud puppy, Ambystoma mexicanum", 31 | "bullfrog, Rana catesbeiana", 32 | "tree frog, tree-frog", 33 | "tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui", 34 | "loggerhead, loggerhead turtle, Caretta caretta", 35 | "leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea", 36 | "mud turtle", 37 | "terrapin", 38 | "box turtle, box tortoise", 39 | "banded gecko", 40 | "common iguana, iguana, Iguana iguana", 41 | "American chameleon, anole, Anolis carolinensis", 42 | "whiptail, whiptail lizard", 43 | "agama", 44 | "frilled lizard, Chlamydosaurus kingi", 45 | "alligator lizard", 46 | "Gila monster, Heloderma suspectum", 47 | "green lizard, Lacerta viridis", 48 | "African chameleon, Chamaeleo chamaeleon", 49 | "Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis", 50 | "African crocodile, Nile crocodile, Crocodylus niloticus", 51 | "American alligator, Alligator mississipiensis", 52 | "triceratops", 53 | "thunder snake, worm snake, Carphophis amoenus", 54 | "ringneck snake, ring-necked snake, ring snake", 55 | "hognose snake, puff adder, sand viper", 56 | "green snake, grass snake", 57 | "king snake, kingsnake", 58 | "garter snake, grass snake", 59 | "water snake", 60 | "vine snake", 61 | "night snake, Hypsiglena torquata", 62 | "boa constrictor, Constrictor constrictor", 63 | "rock python, rock snake, Python sebae", 64 | "Indian cobra, Naja naja", 65 | "green mamba", 66 | "sea snake", 67 | "horned viper, cerastes, sand viper, horned asp, Cerastes cornutus", 68 | "diamondback, diamondback rattlesnake, Crotalus adamanteus", 69 | "sidewinder, horned rattlesnake, Crotalus cerastes", 70 | "trilobite", 71 | "harvestman, daddy longlegs, Phalangium opilio", 72 | "scorpion", 73 | "black and gold garden spider, Argiope aurantia", 74 | "barn spider, Araneus cavaticus", 75 | "garden spider, Aranea diademata", 76 | "black widow, Latrodectus mactans", 77 | "tarantula", 78 | "wolf spider, hunting spider", 79 | "tick", 80 | "centipede", 81 | "black grouse", 82 | "ptarmigan", 83 | "ruffed grouse, partridge, Bonasa umbellus", 84 | "prairie chicken, prairie grouse, prairie fowl", 85 | "peacock", 86 | "quail", 87 | "partridge", 88 | "African grey, African gray, Psittacus erithacus", 89 | "macaw", 90 | "sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita", 91 | "lorikeet", 92 | "coucal", 93 | "bee eater", 94 | "hornbill", 95 | "hummingbird", 96 | "jacamar", 97 | "toucan", 98 | "drake", 99 | "red-breasted merganser, Mergus serrator", 100 | "goose", 101 | "black swan, Cygnus atratus", 102 | "tusker", 103 | "echidna, spiny anteater, anteater", 104 | "platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus", 105 | "wallaby, brush kangaroo", 106 | "koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus", 107 | "wombat", 108 | "jellyfish", 109 | "sea anemone, anemone", 110 | "brain coral", 111 | "flatworm, platyhelminth", 112 | "nematode, nematode worm, roundworm", 113 | "conch", 114 | "snail", 115 | "slug", 116 | "sea slug, nudibranch", 117 | "chiton, coat-of-mail shell, sea cradle, polyplacophore", 118 | "chambered nautilus, pearly nautilus, nautilus", 119 | "Dungeness crab, Cancer magister", 120 | "rock crab, Cancer irroratus", 121 | "fiddler crab", 122 | "king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica", 123 | "American lobster, Northern lobster, Maine lobster, Homarus americanus", 124 | "spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish", 125 | "crayfish, crawfish, crawdad, crawdaddy", 126 | "hermit crab", 127 | "isopod", 128 | "white stork, Ciconia ciconia", 129 | "black stork, Ciconia nigra", 130 | "spoonbill", 131 | "flamingo", 132 | "little blue heron, Egretta caerulea", 133 | "American egret, great white heron, Egretta albus", 134 | "bittern", 135 | "crane", 136 | "limpkin, Aramus pictus", 137 | "European gallinule, Porphyrio porphyrio", 138 | "American coot, marsh hen, mud hen, water hen, Fulica americana", 139 | "bustard", 140 | "ruddy turnstone, Arenaria interpres", 141 | "red-backed sandpiper, dunlin, Erolia alpina", 142 | "redshank, Tringa totanus", 143 | "dowitcher", 144 | "oystercatcher, oyster catcher", 145 | "pelican", 146 | "king penguin, Aptenodytes patagonica", 147 | "albatross, mollymawk", 148 | "grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus", 149 | "killer whale, killer, orca, grampus, sea wolf, Orcinus orca", 150 | "dugong, Dugong dugon", 151 | "sea lion", 152 | "Chihuahua", 153 | "Japanese spaniel", 154 | "Maltese dog, Maltese terrier, Maltese", 155 | "Pekinese, Pekingese, Peke", 156 | "Shih-Tzu", 157 | "Blenheim spaniel", 158 | "papillon", 159 | "toy terrier", 160 | "Rhodesian ridgeback", 161 | "Afghan hound, Afghan", 162 | "basset, basset hound", 163 | "beagle", 164 | "bloodhound, sleuthhound", 165 | "bluetick", 166 | "black-and-tan coonhound", 167 | "Walker hound, Walker foxhound", 168 | "English foxhound", 169 | "redbone", 170 | "borzoi, Russian wolfhound", 171 | "Irish wolfhound", 172 | "Italian greyhound", 173 | "whippet", 174 | "Ibizan hound, Ibizan Podenco", 175 | "Norwegian elkhound, elkhound", 176 | "otterhound, otter hound", 177 | "Saluki, gazelle hound", 178 | "Scottish deerhound, deerhound", 179 | "Weimaraner", 180 | "Staffordshire bullterrier, Staffordshire bull terrier", 181 | "American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier", 182 | "Bedlington terrier", 183 | "Border terrier", 184 | "Kerry blue terrier", 185 | "Irish terrier", 186 | "Norfolk terrier", 187 | "Norwich terrier", 188 | "Yorkshire terrier", 189 | "wire-haired fox terrier", 190 | "Lakeland terrier", 191 | "Sealyham terrier, Sealyham", 192 | "Airedale, Airedale terrier", 193 | "cairn, cairn terrier", 194 | "Australian terrier", 195 | "Dandie Dinmont, Dandie Dinmont terrier", 196 | "Boston bull, Boston terrier", 197 | "miniature schnauzer", 198 | "giant schnauzer", 199 | "standard schnauzer", 200 | "Scotch terrier, Scottish terrier, Scottie", 201 | "Tibetan terrier, chrysanthemum dog", 202 | "silky terrier, Sydney silky", 203 | "soft-coated wheaten terrier", 204 | "West Highland white terrier", 205 | "Lhasa, Lhasa apso", 206 | "flat-coated retriever", 207 | "curly-coated retriever", 208 | "golden retriever", 209 | "Labrador retriever", 210 | "Chesapeake Bay retriever", 211 | "German short-haired pointer", 212 | "vizsla, Hungarian pointer", 213 | "English setter", 214 | "Irish setter, red setter", 215 | "Gordon setter", 216 | "Brittany spaniel", 217 | "clumber, clumber spaniel", 218 | "English springer, English springer spaniel", 219 | "Welsh springer spaniel", 220 | "cocker spaniel, English cocker spaniel, cocker", 221 | "Sussex spaniel", 222 | "Irish water spaniel", 223 | "kuvasz", 224 | "schipperke", 225 | "groenendael", 226 | "malinois", 227 | "briard", 228 | "kelpie", 229 | "komondor", 230 | "Old English sheepdog, bobtail", 231 | "Shetland sheepdog, Shetland sheep dog, Shetland", 232 | "collie", 233 | "Border collie", 234 | "Bouvier des Flandres, Bouviers des Flandres", 235 | "Rottweiler", 236 | "German shepherd, German shepherd dog, German police dog, alsatian", 237 | "Doberman, Doberman pinscher", 238 | "miniature pinscher", 239 | "Greater Swiss Mountain dog", 240 | "Bernese mountain dog", 241 | "Appenzeller", 242 | "EntleBucher", 243 | "boxer", 244 | "bull mastiff", 245 | "Tibetan mastiff", 246 | "French bulldog", 247 | "Great Dane", 248 | "Saint Bernard, St Bernard", 249 | "Eskimo dog, husky", 250 | "malamute, malemute, Alaskan malamute", 251 | "Siberian husky", 252 | "dalmatian, coach dog, carriage dog", 253 | "affenpinscher, monkey pinscher, monkey dog", 254 | "basenji", 255 | "pug, pug-dog", 256 | "Leonberg", 257 | "Newfoundland, Newfoundland dog", 258 | "Great Pyrenees", 259 | "Samoyed, Samoyede", 260 | "Pomeranian", 261 | "chow, chow chow", 262 | "keeshond", 263 | "Brabancon griffon", 264 | "Pembroke, Pembroke Welsh corgi", 265 | "Cardigan, Cardigan Welsh corgi", 266 | "toy poodle", 267 | "miniature poodle", 268 | "standard poodle", 269 | "Mexican hairless", 270 | "timber wolf, grey wolf, gray wolf, Canis lupus", 271 | "white wolf, Arctic wolf, Canis lupus tundrarum", 272 | "red wolf, maned wolf, Canis rufus, Canis niger", 273 | "coyote, prairie wolf, brush wolf, Canis latrans", 274 | "dingo, warrigal, warragal, Canis dingo", 275 | "dhole, Cuon alpinus", 276 | "African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus", 277 | "hyena, hyaena", 278 | "red fox, Vulpes vulpes", 279 | "kit fox, Vulpes macrotis", 280 | "Arctic fox, white fox, Alopex lagopus", 281 | "grey fox, gray fox, Urocyon cinereoargenteus", 282 | "tabby, tabby cat", 283 | "tiger cat", 284 | "Persian cat", 285 | "Siamese cat, Siamese", 286 | "Egyptian cat", 287 | "cougar, puma, catamount, mountain lion, painter, panther, Felis concolor", 288 | "lynx, catamount", 289 | "leopard, Panthera pardus", 290 | "snow leopard, ounce, Panthera uncia", 291 | "jaguar, panther, Panthera onca, Felis onca", 292 | "lion, king of beasts, Panthera leo", 293 | "tiger, Panthera tigris", 294 | "cheetah, chetah, Acinonyx jubatus", 295 | "brown bear, bruin, Ursus arctos", 296 | "American black bear, black bear, Ursus americanus, Euarctos americanus", 297 | "ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus", 298 | "sloth bear, Melursus ursinus, Ursus ursinus", 299 | "mongoose", 300 | "meerkat, mierkat", 301 | "tiger beetle", 302 | "ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle", 303 | "ground beetle, carabid beetle", 304 | "long-horned beetle, longicorn, longicorn beetle", 305 | "leaf beetle, chrysomelid", 306 | "dung beetle", 307 | "rhinoceros beetle", 308 | "weevil", 309 | "fly", 310 | "bee", 311 | "ant, emmet, pismire", 312 | "grasshopper, hopper", 313 | "cricket", 314 | "walking stick, walkingstick, stick insect", 315 | "cockroach, roach", 316 | "mantis, mantid", 317 | "cicada, cicala", 318 | "leafhopper", 319 | "lacewing, lacewing fly", 320 | "dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk", 321 | "damselfly", 322 | "admiral", 323 | "ringlet, ringlet butterfly", 324 | "monarch, monarch butterfly, milkweed butterfly, Danaus plexippus", 325 | "cabbage butterfly", 326 | "sulphur butterfly, sulfur butterfly", 327 | "lycaenid, lycaenid butterfly", 328 | "starfish, sea star", 329 | "sea urchin", 330 | "sea cucumber, holothurian", 331 | "wood rabbit, cottontail, cottontail rabbit", 332 | "hare", 333 | "Angora, Angora rabbit", 334 | "hamster", 335 | "porcupine, hedgehog", 336 | "fox squirrel, eastern fox squirrel, Sciurus niger", 337 | "marmot", 338 | "beaver", 339 | "guinea pig, Cavia cobaya", 340 | "sorrel", 341 | "zebra", 342 | "hog, pig, grunter, squealer, Sus scrofa", 343 | "wild boar, boar, Sus scrofa", 344 | "warthog", 345 | "hippopotamus, hippo, river horse, Hippopotamus amphibius", 346 | "ox", 347 | "water buffalo, water ox, Asiatic buffalo, Bubalus bubalis", 348 | "bison", 349 | "ram, tup", 350 | "bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis", 351 | "ibex, Capra ibex", 352 | "hartebeest", 353 | "impala, Aepyceros melampus", 354 | "gazelle", 355 | "Arabian camel, dromedary, Camelus dromedarius", 356 | "llama", 357 | "weasel", 358 | "mink", 359 | "polecat, fitch, foulmart, foumart, Mustela putorius", 360 | "black-footed ferret, ferret, Mustela nigripes", 361 | "otter", 362 | "skunk, polecat, wood pussy", 363 | "badger", 364 | "armadillo", 365 | "three-toed sloth, ai, Bradypus tridactylus", 366 | "orangutan, orang, orangutang, Pongo pygmaeus", 367 | "gorilla, Gorilla gorilla", 368 | "chimpanzee, chimp, Pan troglodytes", 369 | "gibbon, Hylobates lar", 370 | "siamang, Hylobates syndactylus, Symphalangus syndactylus", 371 | "guenon, guenon monkey", 372 | "patas, hussar monkey, Erythrocebus patas", 373 | "baboon", 374 | "macaque", 375 | "langur", 376 | "colobus, colobus monkey", 377 | "proboscis monkey, Nasalis larvatus", 378 | "marmoset", 379 | "capuchin, ringtail, Cebus capucinus", 380 | "howler monkey, howler", 381 | "titi, titi monkey", 382 | "spider monkey, Ateles geoffroyi", 383 | "squirrel monkey, Saimiri sciureus", 384 | "Madagascar cat, ring-tailed lemur, Lemur catta", 385 | "indri, indris, Indri indri, Indri brevicaudatus", 386 | "Indian elephant, Elephas maximus", 387 | "African elephant, Loxodonta africana", 388 | "lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens", 389 | "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca", 390 | "barracouta, snoek", 391 | "eel", 392 | "coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch", 393 | "rock beauty, Holocanthus tricolor", 394 | "anemone fish", 395 | "sturgeon", 396 | "gar, garfish, garpike, billfish, Lepisosteus osseus", 397 | "lionfish", 398 | "puffer, pufferfish, blowfish, globefish", 399 | "abacus", 400 | "abaya", 401 | "academic gown, academic robe, judge's robe", 402 | "accordion, piano accordion, squeeze box", 403 | "acoustic guitar", 404 | "aircraft carrier, carrier, flattop, attack aircraft carrier", 405 | "airliner", 406 | "airship, dirigible", 407 | "altar", 408 | "ambulance", 409 | "amphibian, amphibious vehicle", 410 | "analog clock", 411 | "apiary, bee house", 412 | "apron", 413 | "ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin", 414 | "assault rifle, assault gun", 415 | "backpack, back pack, knapsack, packsack, rucksack, haversack", 416 | "bakery, bakeshop, bakehouse", 417 | "balance beam, beam", 418 | "balloon", 419 | "ballpoint, ballpoint pen, ballpen, Biro", 420 | "Band Aid", 421 | "banjo", 422 | "bannister, banister, balustrade, balusters, handrail", 423 | "barbell", 424 | "barber chair", 425 | "barbershop", 426 | "barn", 427 | "barometer", 428 | "barrel, cask", 429 | "barrow, garden cart, lawn cart, wheelbarrow", 430 | "baseball", 431 | "basketball", 432 | "bassinet", 433 | "bassoon", 434 | "bathing cap, swimming cap", 435 | "bath towel", 436 | "bathtub, bathing tub, bath, tub", 437 | "beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon", 438 | "beacon, lighthouse, beacon light, pharos", 439 | "beaker", 440 | "bearskin, busby, shako", 441 | "beer bottle", 442 | "beer glass", 443 | "bell cote, bell cot", 444 | "bib", 445 | "bicycle-built-for-two, tandem bicycle, tandem", 446 | "bikini, two-piece", 447 | "binder, ring-binder", 448 | "binoculars, field glasses, opera glasses", 449 | "birdhouse", 450 | "boathouse", 451 | "bobsled, bobsleigh, bob", 452 | "bolo tie, bolo, bola tie, bola", 453 | "bonnet, poke bonnet", 454 | "bookcase", 455 | "bookshop, bookstore, bookstall", 456 | "bottlecap", 457 | "bow", 458 | "bow tie, bow-tie, bowtie", 459 | "brass, memorial tablet, plaque", 460 | "brassiere, bra, bandeau", 461 | "breakwater, groin, groyne, mole, bulwark, seawall, jetty", 462 | "breastplate, aegis, egis", 463 | "broom", 464 | "bucket, pail", 465 | "buckle", 466 | "bulletproof vest", 467 | "bullet train, bullet", 468 | "butcher shop, meat market", 469 | "cab, hack, taxi, taxicab", 470 | "caldron, cauldron", 471 | "candle, taper, wax light", 472 | "cannon", 473 | "canoe", 474 | "can opener, tin opener", 475 | "cardigan", 476 | "car mirror", 477 | "carousel, carrousel, merry-go-round, roundabout, whirligig", 478 | "carpenter's kit, tool kit", 479 | "carton", 480 | "car wheel", 481 | "cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM", 482 | "cassette", 483 | "cassette player", 484 | "castle", 485 | "catamaran", 486 | "CD player", 487 | "cello, violoncello", 488 | "cellular telephone, cellular phone, cellphone, cell, mobile phone", 489 | "chain", 490 | "chainlink fence", 491 | "chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour", 492 | "chain saw, chainsaw", 493 | "chest", 494 | "chiffonier, commode", 495 | "chime, bell, gong", 496 | "china cabinet, china closet", 497 | "Christmas stocking", 498 | "church, church building", 499 | "cinema, movie theater, movie theatre, movie house, picture palace", 500 | "cleaver, meat cleaver, chopper", 501 | "cliff dwelling", 502 | "cloak", 503 | "clog, geta, patten, sabot", 504 | "cocktail shaker", 505 | "coffee mug", 506 | "coffeepot", 507 | "coil, spiral, volute, whorl, helix", 508 | "combination lock", 509 | "computer keyboard, keypad", 510 | "confectionery, confectionary, candy store", 511 | "container ship, containership, container vessel", 512 | "convertible", 513 | "corkscrew, bottle screw", 514 | "cornet, horn, trumpet, trump", 515 | "cowboy boot", 516 | "cowboy hat, ten-gallon hat", 517 | "cradle", 518 | "crane", 519 | "crash helmet", 520 | "crate", 521 | "crib, cot", 522 | "Crock Pot", 523 | "croquet ball", 524 | "crutch", 525 | "cuirass", 526 | "dam, dike, dyke", 527 | "desk", 528 | "desktop computer", 529 | "dial telephone, dial phone", 530 | "diaper, nappy, napkin", 531 | "digital clock", 532 | "digital watch", 533 | "dining table, board", 534 | "dishrag, dishcloth", 535 | "dishwasher, dish washer, dishwashing machine", 536 | "disk brake, disc brake", 537 | "dock, dockage, docking facility", 538 | "dogsled, dog sled, dog sleigh", 539 | "dome", 540 | "doormat, welcome mat", 541 | "drilling platform, offshore rig", 542 | "drum, membranophone, tympan", 543 | "drumstick", 544 | "dumbbell", 545 | "Dutch oven", 546 | "electric fan, blower", 547 | "electric guitar", 548 | "electric locomotive", 549 | "entertainment center", 550 | "envelope", 551 | "espresso maker", 552 | "face powder", 553 | "feather boa, boa", 554 | "file, file cabinet, filing cabinet", 555 | "fireboat", 556 | "fire engine, fire truck", 557 | "fire screen, fireguard", 558 | "flagpole, flagstaff", 559 | "flute, transverse flute", 560 | "folding chair", 561 | "football helmet", 562 | "forklift", 563 | "fountain", 564 | "fountain pen", 565 | "four-poster", 566 | "freight car", 567 | "French horn, horn", 568 | "frying pan, frypan, skillet", 569 | "fur coat", 570 | "garbage truck, dustcart", 571 | "gasmask, respirator, gas helmet", 572 | "gas pump, gasoline pump, petrol pump, island dispenser", 573 | "goblet", 574 | "go-kart", 575 | "golf ball", 576 | "golfcart, golf cart", 577 | "gondola", 578 | "gong, tam-tam", 579 | "gown", 580 | "grand piano, grand", 581 | "greenhouse, nursery, glasshouse", 582 | "grille, radiator grille", 583 | "grocery store, grocery, food market, market", 584 | "guillotine", 585 | "hair slide", 586 | "hair spray", 587 | "half track", 588 | "hammer", 589 | "hamper", 590 | "hand blower, blow dryer, blow drier, hair dryer, hair drier", 591 | "hand-held computer, hand-held microcomputer", 592 | "handkerchief, hankie, hanky, hankey", 593 | "hard disc, hard disk, fixed disk", 594 | "harmonica, mouth organ, harp, mouth harp", 595 | "harp", 596 | "harvester, reaper", 597 | "hatchet", 598 | "holster", 599 | "home theater, home theatre", 600 | "honeycomb", 601 | "hook, claw", 602 | "hoopskirt, crinoline", 603 | "horizontal bar, high bar", 604 | "horse cart, horse-cart", 605 | "hourglass", 606 | "iPod", 607 | "iron, smoothing iron", 608 | "jack-o-lantern", 609 | "jean, blue jean, denim", 610 | "jeep, landrover", 611 | "jersey, T-shirt, tee shirt", 612 | "jigsaw puzzle", 613 | "jinrikisha, ricksha, rickshaw", 614 | "joystick", 615 | "kimono", 616 | "knee pad", 617 | "knot", 618 | "lab coat, laboratory coat", 619 | "ladle", 620 | "lampshade, lamp shade", 621 | "laptop, laptop computer", 622 | "lawn mower, mower", 623 | "lens cap, lens cover", 624 | "letter opener, paper knife, paperknife", 625 | "library", 626 | "lifeboat", 627 | "lighter, light, igniter, ignitor", 628 | "limousine, limo", 629 | "liner, ocean liner", 630 | "lipstick, lip rouge", 631 | "Loafer", 632 | "lotion", 633 | "loudspeaker, speaker, speaker unit, loudspeaker system, speaker system", 634 | "loupe, jeweler's loupe", 635 | "lumbermill, sawmill", 636 | "magnetic compass", 637 | "mailbag, postbag", 638 | "mailbox, letter box", 639 | "maillot", 640 | "maillot, tank suit", 641 | "manhole cover", 642 | "maraca", 643 | "marimba, xylophone", 644 | "mask", 645 | "matchstick", 646 | "maypole", 647 | "maze, labyrinth", 648 | "measuring cup", 649 | "medicine chest, medicine cabinet", 650 | "megalith, megalithic structure", 651 | "microphone, mike", 652 | "microwave, microwave oven", 653 | "military uniform", 654 | "milk can", 655 | "minibus", 656 | "miniskirt, mini", 657 | "minivan", 658 | "missile", 659 | "mitten", 660 | "mixing bowl", 661 | "mobile home, manufactured home", 662 | "Model T", 663 | "modem", 664 | "monastery", 665 | "monitor", 666 | "moped", 667 | "mortar", 668 | "mortarboard", 669 | "mosque", 670 | "mosquito net", 671 | "motor scooter, scooter", 672 | "mountain bike, all-terrain bike, off-roader", 673 | "mountain tent", 674 | "mouse, computer mouse", 675 | "mousetrap", 676 | "moving van", 677 | "muzzle", 678 | "nail", 679 | "neck brace", 680 | "necklace", 681 | "nipple", 682 | "notebook, notebook computer", 683 | "obelisk", 684 | "oboe, hautboy, hautbois", 685 | "ocarina, sweet potato", 686 | "odometer, hodometer, mileometer, milometer", 687 | "oil filter", 688 | "organ, pipe organ", 689 | "oscilloscope, scope, cathode-ray oscilloscope, CRO", 690 | "overskirt", 691 | "oxcart", 692 | "oxygen mask", 693 | "packet", 694 | "paddle, boat paddle", 695 | "paddlewheel, paddle wheel", 696 | "padlock", 697 | "paintbrush", 698 | "pajama, pyjama, pjs, jammies", 699 | "palace", 700 | "panpipe, pandean pipe, syrinx", 701 | "paper towel", 702 | "parachute, chute", 703 | "parallel bars, bars", 704 | "park bench", 705 | "parking meter", 706 | "passenger car, coach, carriage", 707 | "patio, terrace", 708 | "pay-phone, pay-station", 709 | "pedestal, plinth, footstall", 710 | "pencil box, pencil case", 711 | "pencil sharpener", 712 | "perfume, essence", 713 | "Petri dish", 714 | "photocopier", 715 | "pick, plectrum, plectron", 716 | "pickelhaube", 717 | "picket fence, paling", 718 | "pickup, pickup truck", 719 | "pier", 720 | "piggy bank, penny bank", 721 | "pill bottle", 722 | "pillow", 723 | "ping-pong ball", 724 | "pinwheel", 725 | "pirate, pirate ship", 726 | "pitcher, ewer", 727 | "plane, carpenter's plane, woodworking plane", 728 | "planetarium", 729 | "plastic bag", 730 | "plate rack", 731 | "plow, plough", 732 | "plunger, plumber's helper", 733 | "Polaroid camera, Polaroid Land camera", 734 | "pole", 735 | "police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria", 736 | "poncho", 737 | "pool table, billiard table, snooker table", 738 | "pop bottle, soda bottle", 739 | "pot, flowerpot", 740 | "potters wheel", 741 | "power drill", 742 | "prayer rug, prayer mat", 743 | "printer", 744 | "prison, prison house", 745 | "projectile, missile", 746 | "projector", 747 | "puck, hockey puck", 748 | "punching bag, punch bag, punching ball, punchball", 749 | "purse", 750 | "quill, quill pen", 751 | "quilt, comforter, comfort, puff", 752 | "racer, race car, racing car", 753 | "racket, racquet", 754 | "radiator", 755 | "radio, wireless", 756 | "radio telescope, radio reflector", 757 | "rain barrel", 758 | "recreational vehicle, RV, R.V.", 759 | "reel", 760 | "reflex camera", 761 | "refrigerator, icebox", 762 | "remote control, remote", 763 | "restaurant, eating house, eating place, eatery", 764 | "revolver, six-gun, six-shooter", 765 | "rifle", 766 | "rocking chair, rocker", 767 | "rotisserie", 768 | "rubber eraser, rubber, pencil eraser", 769 | "rugby ball", 770 | "rule, ruler", 771 | "running shoe", 772 | "safe", 773 | "safety pin", 774 | "saltshaker, salt shaker", 775 | "sandal", 776 | "sarong", 777 | "sax, saxophone", 778 | "scabbard", 779 | "scale, weighing machine", 780 | "school bus", 781 | "schooner", 782 | "scoreboard", 783 | "screen, CRT screen", 784 | "screw", 785 | "screwdriver", 786 | "seat belt, seatbelt", 787 | "sewing machine", 788 | "shield, buckler", 789 | "shoe shop, shoe-shop, shoe store", 790 | "shoji", 791 | "shopping basket", 792 | "shopping cart", 793 | "shovel", 794 | "shower cap", 795 | "shower curtain", 796 | "ski", 797 | "ski mask", 798 | "sleeping bag", 799 | "slide rule, slipstick", 800 | "sliding door", 801 | "slot, one-armed bandit", 802 | "snorkel", 803 | "snowmobile", 804 | "snowplow, snowplough", 805 | "soap dispenser", 806 | "soccer ball", 807 | "sock", 808 | "solar dish, solar collector, solar furnace", 809 | "sombrero", 810 | "soup bowl", 811 | "space bar", 812 | "space heater", 813 | "space shuttle", 814 | "spatula", 815 | "speedboat", 816 | "spider web, spider web", 817 | "spindle", 818 | "sports car, sport car", 819 | "spotlight, spot", 820 | "stage", 821 | "steam locomotive", 822 | "steel arch bridge", 823 | "steel drum", 824 | "stethoscope", 825 | "stole", 826 | "stone wall", 827 | "stopwatch, stop watch", 828 | "stove", 829 | "strainer", 830 | "streetcar, tram, tramcar, trolley, trolley car", 831 | "stretcher", 832 | "studio couch, day bed", 833 | "stupa, tope", 834 | "submarine, pigboat, sub, U-boat", 835 | "suit, suit of clothes", 836 | "sundial", 837 | "sunglass", 838 | "sunglasses, dark glasses, shades", 839 | "sunscreen, sunblock, sun blocker", 840 | "suspension bridge", 841 | "swab, swob, mop", 842 | "sweatshirt", 843 | "swimming trunks, bathing trunks", 844 | "swing", 845 | "switch, electric switch, electrical switch", 846 | "syringe", 847 | "table lamp", 848 | "tank, army tank, armored combat vehicle, armoured combat vehicle", 849 | "tape player", 850 | "teapot", 851 | "teddy, teddy bear", 852 | "television, television system", 853 | "tennis ball", 854 | "thatch, thatched roof", 855 | "theater curtain, theatre curtain", 856 | "thimble", 857 | "thresher, thrasher, threshing machine", 858 | "throne", 859 | "tile roof", 860 | "toaster", 861 | "tobacco shop, tobacconist shop, tobacconist", 862 | "toilet seat", 863 | "torch", 864 | "totem pole", 865 | "tow truck, tow car, wrecker", 866 | "toyshop", 867 | "tractor", 868 | "trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi", 869 | "tray", 870 | "trench coat", 871 | "tricycle, trike, velocipede", 872 | "trimaran", 873 | "tripod", 874 | "triumphal arch", 875 | "trolleybus, trolley coach, trackless trolley", 876 | "trombone", 877 | "tub, vat", 878 | "turnstile", 879 | "typewriter keyboard", 880 | "umbrella", 881 | "unicycle, monocycle", 882 | "upright, upright piano", 883 | "vacuum, vacuum cleaner", 884 | "vase", 885 | "vault", 886 | "velvet", 887 | "vending machine", 888 | "vestment", 889 | "viaduct", 890 | "violin, fiddle", 891 | "volleyball", 892 | "waffle iron", 893 | "wall clock", 894 | "wallet, billfold, notecase, pocketbook", 895 | "wardrobe, closet, press", 896 | "warplane, military plane", 897 | "washbasin, handbasin, washbowl, lavabo, wash-hand basin", 898 | "washer, automatic washer, washing machine", 899 | "water bottle", 900 | "water jug", 901 | "water tower", 902 | "whiskey jug", 903 | "whistle", 904 | "wig", 905 | "window screen", 906 | "window shade", 907 | "Windsor tie", 908 | "wine bottle", 909 | "wing", 910 | "wok", 911 | "wooden spoon", 912 | "wool, woolen, woollen", 913 | "worm fence, snake fence, snake-rail fence, Virginia fence", 914 | "wreck", 915 | "yawl", 916 | "yurt", 917 | "web site, website, internet site, site", 918 | "comic book", 919 | "crossword puzzle, crossword", 920 | "street sign", 921 | "traffic light, traffic signal, stoplight", 922 | "book jacket, dust cover, dust jacket, dust wrapper", 923 | "menu", 924 | "plate", 925 | "guacamole", 926 | "consomme", 927 | "hot pot, hotpot", 928 | "trifle", 929 | "ice cream, icecream", 930 | "ice lolly, lolly, lollipop, popsicle", 931 | "French loaf", 932 | "bagel, beigel", 933 | "pretzel", 934 | "cheeseburger", 935 | "hotdog, hot dog, red hot", 936 | "mashed potato", 937 | "head cabbage", 938 | "broccoli", 939 | "cauliflower", 940 | "zucchini, courgette", 941 | "spaghetti squash", 942 | "acorn squash", 943 | "butternut squash", 944 | "cucumber, cuke", 945 | "artichoke, globe artichoke", 946 | "bell pepper", 947 | "cardoon", 948 | "mushroom", 949 | "Granny Smith", 950 | "strawberry", 951 | "orange", 952 | "lemon", 953 | "fig", 954 | "pineapple, ananas", 955 | "banana", 956 | "jackfruit, jak, jack", 957 | "custard apple", 958 | "pomegranate", 959 | "hay", 960 | "carbonara", 961 | "chocolate sauce, chocolate syrup", 962 | "dough", 963 | "meat loaf, meatloaf", 964 | "pizza, pizza pie", 965 | "potpie", 966 | "burrito", 967 | "red wine", 968 | "espresso", 969 | "cup", 970 | "eggnog", 971 | "alp", 972 | "bubble", 973 | "cliff, drop, drop-off", 974 | "coral reef", 975 | "geyser", 976 | "lakeside, lakeshore", 977 | "promontory, headland, head, foreland", 978 | "sandbar, sand bar", 979 | "seashore, coast, seacoast, sea-coast", 980 | "valley, vale", 981 | "volcano", 982 | "ballplayer, baseball player", 983 | "groom, bridegroom", 984 | "scuba diver", 985 | "rapeseed", 986 | "daisy", 987 | "yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum", 988 | "corn", 989 | "acorn", 990 | "hip, rose hip, rosehip", 991 | "buckeye, horse chestnut, conker", 992 | "coral fungus", 993 | "agaric", 994 | "gyromitra", 995 | "stinkhorn, carrion fungus", 996 | "earthstar", 997 | "hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa", 998 | "bolete", 999 | "ear, spike, capitulum", 1000 | "toilet tissue, toilet paper, bathroom tissue" 1001 | -------------------------------------------------------------------------------- /examples/resnet-18/resnet-18.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include "../../vendors/stb_image.h" 3 | #define STB_IMAGE_WRITE_IMPLEMENTATION 4 | #include "../../vendors/stb_image_write.h" 5 | 6 | // Comment line below to enable logging and assertion 7 | #define NDEBUG 8 | 9 | #define MT_USE_IM2COL_CONV 10 | #define MT_USE_STB_IMAGE 11 | #define MT_USE_STB_IMAGE_WRITE 12 | #define MT_IMPLEMENTATION 13 | 14 | /* 15 | uncomment one of lines below as needed 16 | */ 17 | // #define MT_USE_NEON 18 | // #define MT_USE_BLAS // make CFLAGS+="-lblas" 19 | // #define MT_USE_PTHREAD 20 | 21 | #include "../../mint.h" 22 | #define MT_ONNX_IMPLEMENTATION 23 | #include "../../mint_onnx.h" 24 | 25 | #include 26 | 27 | int main(int argc, char **argv) { 28 | // clang-format off 29 | char *class_labels[] = { 30 | #include "class-list.inc" 31 | }; 32 | // clang-format on 33 | 34 | if (argc != 3) { 35 | printf("This program requires 2 arguments: 1) the path to resnet.mt " 36 | "and 2) the path image to predict"); 37 | exit(1); 38 | } 39 | 40 | char *model_path = argv[1]; 41 | char *image_path = argv[2]; 42 | 43 | clock_t start = clock(); 44 | mt_model *model = NULL; 45 | if (strstr(model_path, ".onnx") != NULL) { 46 | model = mt_onnx_read_file(model_path); 47 | } else { 48 | model = mt_model_load(model_path); 49 | } 50 | clock_t end = clock(); 51 | double time_spent = (double)(end - start) / CLOCKS_PER_SEC; 52 | printf("Model loading time: %.3f seconds\n", time_spent); 53 | 54 | if (model == NULL) { 55 | ERROR("Failed to load model"); 56 | exit(1); 57 | } 58 | mt_tensor *image = mt_tensor_load_image(image_path); 59 | mt_tensor *image_resized = mt_image_resize(image, 224, 224); 60 | 61 | float *mean = (float[]){0.485, 0.456, 0.406}; 62 | float *std = (float[]){0.229, 0.224, 0.225}; 63 | mt_image_standardize(image_resized, mean, std); 64 | 65 | mt_tensor_unsqueeze_inplace(image_resized, 0); 66 | 67 | mt_model_set_input(model, "input", image_resized); 68 | mt_model_run(model, NULL, NULL); 69 | 70 | mt_tensor *output = mt_model_get_output(model, "output"); 71 | 72 | int arg_max = 0; 73 | mt_float cur_max = output->data[arg_max]; 74 | for (int i = 0; i < mt_tensor_count_element(output); ++i) { 75 | if (output->data[i] > cur_max) { 76 | cur_max = output->data[i]; 77 | arg_max = i; 78 | } 79 | } 80 | printf("Predicted class: %s\n", class_labels[arg_max]); 81 | 82 | mt_tensor_free(output); 83 | mt_tensor_free(image_resized); 84 | mt_tensor_free(image); 85 | mt_model_free(model); 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /examples/simple/Makefile: -------------------------------------------------------------------------------- 1 | CC ?= gcc 2 | 3 | TARGET = simple 4 | SRC = $(TARGET).c 5 | 6 | $(TARGET).out: $(SRC) 7 | $(CC) $(CFLAGS) -o $@ $< 8 | 9 | clean: 10 | rm -f $(TARGET).out 11 | -------------------------------------------------------------------------------- /examples/simple/simple.c: -------------------------------------------------------------------------------- 1 | // clang-format off 2 | #define MT_IMPLEMENTATION 3 | #include "../../mint.h" 4 | 5 | int main() { 6 | /***************************************************************** 7 | * NOTE: All tensor values are stored in row-major order 8 | *****************************************************************/ 9 | 10 | // Create tensor with values 11 | // ------------------------- 12 | // Creating a tensor with specified values can be done using 13 | // mt_tensor_alloc_values(). For example, to create a 2 by 2 14 | // 2 dimensional tensor (a matrix) we can do this: 15 | mt_tensor *t = mt_tensor_alloc_values( 16 | (int[]){2, 2}, // shape 17 | 2, // number of dimensions 18 | (float[]){1, 2, 3, 4} // the values 19 | ); 20 | 21 | // You can access the data directly like this 22 | for (int i = 0; i< mt_tensor_count_element(t); ++i) { 23 | printf("%f\n", t->data[i]); 24 | } 25 | 26 | // There is also a helper function to print tensor 27 | mt_tensor_print(t); 28 | 29 | // Don't forget to free accordingly. 30 | mt_tensor_free(t); 31 | 32 | 33 | /***************************************************************** 34 | * Simple arithmetic operations 35 | *****************************************************************/ 36 | 37 | // Create two tensors 38 | mt_tensor *a = mt_tensor_alloc_values( 39 | (int[]){2, 2}, 40 | 2, 41 | (float[]){1, 2, 3, 4} 42 | ); 43 | 44 | mt_tensor *b = mt_tensor_alloc_values( 45 | (int[]){2, 2}, 46 | 2, 47 | (float[]){5, 6, 7, 8} 48 | ); 49 | 50 | // Add the two tensors 51 | mt_tensor *c = mt_add(a, b); 52 | 53 | // Print the result 54 | printf("a + b = \n"); 55 | mt_tensor_print(c); 56 | 57 | mt_tensor_free(a), mt_tensor_free(b), mt_tensor_free(c); 58 | 59 | /***************************************************************** 60 | * Broadcasting 61 | *****************************************************************/ 62 | 63 | // Let's add two tensors with different shapes, where a is of shape 64 | // (1, 4) and b is of shape (4, 1). Broadcasting will be applied to 65 | // make the shapes compatible (like Python's numpy). The result will 66 | // be a tensor of shape (4, 4). 67 | 68 | a = mt_tensor_alloc_values( 69 | (int[]){1, 4}, 70 | 2, 71 | (float[]){1, 2, 3, 4} 72 | ); 73 | 74 | b = mt_tensor_alloc_values( 75 | (int[]){4, 1}, 76 | 2, 77 | (float[]){1, 2, 3, 4} 78 | ); 79 | 80 | // (Element-wise) multiply the two tensors 81 | c = mt_mul(a, b); 82 | 83 | // Print the result 84 | printf("a * b = \n"); 85 | mt_tensor_print(c); 86 | 87 | // Free the tensors 88 | mt_tensor_free(a), mt_tensor_free(b), mt_tensor_free(c); 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /mint_onnx.h: -------------------------------------------------------------------------------- 1 | #ifndef MINT_ONNX_H 2 | #define MINT_ONNX_H 3 | 4 | #include "mint.h" 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | MTDEF mt_model *mt_onnx_read_file(const char *filename); 12 | MTDEF mt_model *mt_onnx_read_mem(unsigned char *model_bytes, 13 | size_t model_bytes_len); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | #ifdef MT_ONNX_IMPLEMENTATION 20 | 21 | #include "onnx/onnx.proto3.pb-c.h" 22 | #include 23 | #include 24 | 25 | #if !defined(UNUSED) 26 | #define UNUSED(x) ((void)(x)) 27 | #endif 28 | 29 | MTDEF mt_layer_kind mt_onnx__get_layer_kind(const char *op_type) { 30 | if (strcmp(op_type, "Conv") == 0) { 31 | return MT_LAYER_CONV_2D; 32 | } else if (strcmp(op_type, "Relu") == 0) { 33 | return MT_LAYER_RELU; 34 | } else if (strcmp(op_type, "MaxPool") == 0) { 35 | return MT_LAYER_MAX_POOL_2D; 36 | } else if (strcmp(op_type, "Add") == 0) { 37 | return MT_LAYER_ADD; 38 | } else if (strcmp(op_type, "Exp") == 0) { 39 | return MT_LAYER_EXP; 40 | } else if (strcmp(op_type, "GlobalAveragePool") == 0) { 41 | return MT_LAYER_GLOBAL_AVG_POOL; 42 | } else if (strcmp(op_type, "Flatten") == 0) { 43 | return MT_LAYER_FLATTEN; 44 | } else if (strcmp(op_type, "Gemm") == 0) { 45 | return MT_LAYER_DENSE; 46 | } else if (strcmp(op_type, "Pad") == 0) { 47 | return MT_LAYER_PAD; 48 | } else if (strcmp(op_type, "InstanceNormalization") == 0) { 49 | return MT_LAYER_INSTANCE_NORMALIZATION; 50 | } else if (strcmp(op_type, "Upsample") == 0) { 51 | return MT_LAYER_UPSAMPLE; 52 | } else if (strcmp(op_type, "Constant") == 0) { 53 | return MT_LAYER_CONSTANT; 54 | } 55 | return MT_LAYER_UNKNOWN; 56 | } 57 | 58 | MTDEF mt_model *mt_onnx_read_file(const char *filename) { 59 | FILE *file = fopen(filename, "rb"); 60 | if (file == NULL) { 61 | ERROR_F("Failed to open file %s", filename); 62 | } 63 | 64 | fseek(file, 0, SEEK_END); 65 | size_t file_size = ftell(file); 66 | fseek(file, 0, SEEK_SET); 67 | unsigned char *model_bytes = (unsigned char *)malloc(file_size); 68 | fread(model_bytes, 1, file_size, file); 69 | fclose(file); 70 | mt_model *model = mt_onnx_read_mem(model_bytes, file_size); 71 | free(model_bytes); 72 | return model; 73 | } 74 | 75 | MTDEF Onnx__TensorProto * 76 | mt_onnx__get_tensor_proto(Onnx__ModelProto *model_proto, const char *name) { 77 | Onnx__TensorProto **initializers = model_proto->graph->initializer; 78 | for (size_t i = 0; i < model_proto->graph->n_initializer; i++) { 79 | Onnx__TensorProto *tensor_proto = initializers[i]; 80 | if (strcmp(tensor_proto->name, name) == 0) { 81 | return tensor_proto; 82 | } 83 | } 84 | ERROR_F("Tensor %s not found", name); 85 | } 86 | 87 | // We maintain table for model's nodes. 88 | typedef struct node_info_t { 89 | int id; 90 | char *name; 91 | char *input_names[MAX_INPUT_OUTPUT_COUNT]; 92 | int input_count; 93 | char *output_names[MAX_INPUT_OUTPUT_COUNT]; 94 | int output_count; 95 | } node_info_t; 96 | 97 | typedef struct tensor_info_t { 98 | int id; 99 | char *name; 100 | } tensor_info_t; 101 | 102 | MTDEF int mt_onnx__get_tensor_id(tensor_info_t *tensor_infos, int n_tensor, 103 | const char *name) { 104 | for (int i = 0; i < n_tensor; i++) { 105 | if (strcmp(tensor_infos[i].name, name) == 0) { 106 | return i; 107 | } 108 | } 109 | return -1; 110 | } 111 | 112 | MTDEF mt_tensor * 113 | mt_onnx__tensor_proto_to_mt_tensor(Onnx__TensorProto *tensor_proto) { 114 | int shape[tensor_proto->n_dims]; 115 | for (size_t i = 0; i < tensor_proto->n_dims; i++) { 116 | shape[i] = tensor_proto->dims[i]; 117 | } 118 | 119 | mt_tensor *tensor = mt_tensor_alloc(shape, tensor_proto->n_dims); 120 | size_t n_element = mt_tensor_count_element(tensor); 121 | 122 | Onnx__TensorProto__DataType dtype = 123 | (Onnx__TensorProto__DataType)tensor_proto->data_type; 124 | 125 | switch (dtype) { 126 | case ONNX__TENSOR_PROTO__DATA_TYPE__FLOAT: 127 | if (tensor_proto->n_float_data > 0) { 128 | for (size_t i = 0; i < n_element && i < tensor_proto->n_float_data; 129 | i++) { 130 | tensor->data[i] = tensor_proto->float_data[i]; 131 | } 132 | } else if (tensor_proto->raw_data.len > 0) { 133 | mt_float *float_data = (mt_float *)tensor_proto->raw_data.data; 134 | mt_float *aligned_data = 135 | (mt_float *)MT_MALLOC(n_element * sizeof(mt_float)); 136 | memcpy(aligned_data, float_data, n_element * sizeof(mt_float)); 137 | memcpy(tensor->data, aligned_data, n_element * sizeof(mt_float)); 138 | free(aligned_data); 139 | } else { 140 | ERROR_F("No data found in tensor %s", tensor_proto->name); 141 | } 142 | break; 143 | case ONNX__TENSOR_PROTO__DATA_TYPE__INT64: 144 | if (tensor_proto->n_int64_data > 0) { 145 | for (size_t i = 0; i < n_element && i < tensor_proto->n_int64_data; 146 | i++) { 147 | tensor->data[i] = (mt_float)tensor_proto->int64_data[i]; 148 | } 149 | } else if (tensor_proto->raw_data.len > 0) { 150 | int64_t *int64_data = (int64_t *)tensor_proto->raw_data.data; 151 | int64_t *aligned_data = 152 | (int64_t *)MT_MALLOC(n_element * sizeof(int64_t)); 153 | if (aligned_data == NULL) { 154 | ERROR("Failed to allocate memory for aligned INT64 data"); 155 | mt_tensor_free(tensor); 156 | return NULL; 157 | } 158 | memcpy(aligned_data, int64_data, n_element * sizeof(int64_t)); 159 | // Cast to float when copying 160 | for (size_t i = 0; i < n_element; i++) { 161 | tensor->data[i] = (mt_float)aligned_data[i]; 162 | } 163 | free(aligned_data); 164 | } else { 165 | ERROR_F("No data found in tensor %s", tensor_proto->name); 166 | } 167 | break; 168 | case ONNX__TENSOR_PROTO__DATA_TYPE__UINT8: 169 | if (tensor_proto->n_int32_data > 0) { 170 | for (size_t i = 0; i < n_element && i < tensor_proto->n_int32_data; 171 | i++) { 172 | tensor->data[i] = 173 | (mt_float)(uint8_t)tensor_proto->int32_data[i]; 174 | } 175 | } else if (tensor_proto->raw_data.len > 0) { 176 | uint8_t *uint8_data = (uint8_t *)tensor_proto->raw_data.data; 177 | // Cast to float when copying 178 | for (size_t i = 0; i < n_element && i < tensor_proto->raw_data.len; 179 | i++) { 180 | tensor->data[i] = (mt_float)uint8_data[i]; 181 | } 182 | } else { 183 | ERROR_F("No data found in tensor %s", tensor_proto->name); 184 | } 185 | break; 186 | default: 187 | ERROR_F("Unsupported data type: %d", dtype); 188 | } 189 | return tensor; 190 | } 191 | 192 | MTDEF void mt_onnx__make_conv(mt_layer *layer, int opset, 193 | Onnx__NodeProto *node_proto) { 194 | layer->data.conv_2d.w_id = layer->inputs[1]; 195 | layer->data.conv_2d.b_id = layer->inputs[2]; 196 | 197 | // Default values: 198 | memcpy(layer->data.conv_2d.dilations, (int[]){1, 1}, 2); 199 | memcpy(layer->data.conv_2d.pads, (int[]){0, 0, 0, 0}, 4); 200 | layer->data.conv_2d.group = 1; 201 | layer->data.conv_2d.auto_pad = 0; 202 | layer->data.conv_2d.stride = 1; 203 | 204 | // Read attributes of conv2d layer 205 | // There is no significant difference from opset 1 to 22, except for 206 | // the type constraints, which are irrelevant to us. 207 | if (opset >= 1 && opset <= 22) { 208 | for (size_t i = 0; i < node_proto->n_attribute; i++) { 209 | Onnx__AttributeProto *attribute_proto = node_proto->attribute[i]; 210 | if (strcmp(attribute_proto->name, "dilations") == 0) { 211 | for (size_t j = 0; j < attribute_proto->n_ints; j++) { 212 | layer->data.conv_2d.dilations[j] = attribute_proto->ints[j]; 213 | } 214 | } else if (strcmp(attribute_proto->name, "pads") == 0) { 215 | for (size_t j = 0; j < attribute_proto->n_ints; j++) { 216 | layer->data.conv_2d.pads[j] = attribute_proto->ints[j]; 217 | } 218 | } else if (strcmp(attribute_proto->name, "group") == 0) { 219 | layer->data.conv_2d.group = attribute_proto->i; 220 | } else if (strcmp(attribute_proto->name, "auto_pad") == 0) { 221 | if (strcmp((const char *)attribute_proto->s.data, "NOTSET") == 222 | 0) { 223 | layer->data.conv_2d.auto_pad = 0; 224 | } else if (strcmp((const char *)attribute_proto->s.data, 225 | "VALID") == 0) { 226 | layer->data.conv_2d.auto_pad = 1; 227 | } else if (strcmp((const char *)attribute_proto->s.data, 228 | "SAME_UPPER") == 0) { 229 | layer->data.conv_2d.auto_pad = 2; 230 | } else if (strcmp((const char *)attribute_proto->s.data, 231 | "SAME_LOWER") == 0) { 232 | layer->data.conv_2d.auto_pad = 3; 233 | } else { 234 | layer->data.conv_2d.auto_pad = 0; // default to NOTSET 235 | } 236 | } else if (strcmp(attribute_proto->name, "strides") == 0) { 237 | // ensure all strides are the same 238 | for (size_t j = 1; j < attribute_proto->n_ints; j++) { 239 | if (attribute_proto->ints[j] != attribute_proto->ints[0]) { 240 | ERROR_F( 241 | "Cannot handle different strides yet: %lld != %lld", 242 | attribute_proto->ints[j], attribute_proto->ints[0]); 243 | exit(1); 244 | } 245 | } 246 | layer->data.conv_2d.stride = attribute_proto->ints[0]; 247 | } 248 | } 249 | } else { 250 | ERROR_F("Opset %d is not supported for %s", opset, node_proto->op_type); 251 | exit(1); 252 | } 253 | } 254 | 255 | MTDEF void mt_onnx__make_dense(mt_model *model, mt_layer *layer, int opset, 256 | Onnx__NodeProto *node_proto) { 257 | layer->data.dense.w_id = layer->inputs[1]; 258 | layer->data.dense.b_id = layer->inputs[2]; 259 | 260 | // Default values: 261 | layer->data.dense.trans_a = 0; 262 | layer->data.dense.trans_b = 0; 263 | 264 | // Read attributes of dense layer: trans_a, trans_b 265 | if (opset >= 1 && opset <= 22) { 266 | for (size_t i = 0; i < node_proto->n_attribute; i++) { 267 | Onnx__AttributeProto *attribute_proto = node_proto->attribute[i]; 268 | if (strcmp(attribute_proto->name, "transA") == 0) { 269 | layer->data.dense.trans_a = attribute_proto->i; 270 | } else if (strcmp(attribute_proto->name, "transB") == 0) { 271 | layer->data.dense.trans_b = attribute_proto->i; 272 | } 273 | } 274 | } else { 275 | ERROR_F("Opset %d is not supported for %s", opset, node_proto->op_type); 276 | exit(1); 277 | } 278 | 279 | // Some providers transpose the weight matrix, so we need to handle that. 280 | if (layer->data.dense.trans_b == 1) { 281 | mt_tensor *w = model->tensors[layer->data.dense.w_id]; 282 | MT_ASSERT_F(w->ndim == 2, "w must be 2 dimensional, got %d", w->ndim); 283 | mt_tensor *wt = mt_tensor_permute_dims(w, (int[]){1, 0}); 284 | mt_tensor_free(w); 285 | model->tensors[layer->data.dense.w_id] = wt; 286 | } 287 | } 288 | 289 | MTDEF void mt_onnx__make_max_pool(mt_layer *layer, int opset, 290 | Onnx__NodeProto *node_proto) { 291 | // Default values: 292 | layer->data.max_pool_2d.auto_pad = 0; 293 | layer->data.max_pool_2d.size = 2; 294 | layer->data.max_pool_2d.stride = 1; 295 | memcpy(layer->data.max_pool_2d.pads, (int[]){0, 0, 0, 0}, 4); 296 | 297 | // Read attributes of max_pool_2d layer: auto_pad, size, stride, pads. 298 | if (opset >= 1 && opset <= 22) { 299 | for (size_t i = 0; i < node_proto->n_attribute; i++) { 300 | Onnx__AttributeProto *attribute_proto = node_proto->attribute[i]; 301 | if (strcmp(attribute_proto->name, "auto_pad") == 0) { 302 | if (strcmp((const char *)attribute_proto->s.data, "NOTSET") == 303 | 0) { 304 | layer->data.max_pool_2d.auto_pad = 0; 305 | } else if (strcmp((const char *)attribute_proto->s.data, 306 | "VALID") == 0) { 307 | layer->data.max_pool_2d.auto_pad = 1; 308 | } else if (strcmp((const char *)attribute_proto->s.data, 309 | "SAME_UPPER") == 0) { 310 | layer->data.max_pool_2d.auto_pad = 2; 311 | } else if (strcmp((const char *)attribute_proto->s.data, 312 | "SAME_LOWER") == 0) { 313 | layer->data.max_pool_2d.auto_pad = 3; 314 | } else { 315 | layer->data.max_pool_2d.auto_pad = 0; // default to NOTSET 316 | } 317 | } else if (strcmp(attribute_proto->name, "kernel_shape") == 0) { 318 | // ensure all kernel_shape are the same 319 | for (size_t j = 1; j < attribute_proto->n_ints; j++) { 320 | if (attribute_proto->ints[j] != attribute_proto->ints[0]) { 321 | ERROR_F("Cannot handle different kernel_shape yet: " 322 | "%lld != %lld", 323 | attribute_proto->ints[j], 324 | attribute_proto->ints[0]); 325 | exit(1); 326 | } 327 | } 328 | layer->data.max_pool_2d.size = attribute_proto->ints[0]; 329 | } else if (strcmp(attribute_proto->name, "strides") == 0) { 330 | // ensure all strides are the same 331 | for (size_t j = 1; j < attribute_proto->n_ints; j++) { 332 | if (attribute_proto->ints[j] != attribute_proto->ints[0]) { 333 | ERROR_F( 334 | "Cannot handle different strides yet: %lld != %lld", 335 | attribute_proto->ints[j], attribute_proto->ints[0]); 336 | exit(1); 337 | } 338 | } 339 | layer->data.max_pool_2d.stride = attribute_proto->ints[0]; 340 | } else if (strcmp(attribute_proto->name, "pads") == 0) { 341 | for (size_t j = 0; j < attribute_proto->n_ints; j++) { 342 | layer->data.max_pool_2d.pads[j] = attribute_proto->ints[j]; 343 | } 344 | } else if (strcmp(attribute_proto->name, "ceil_mode") == 0) { 345 | // Ensure ceil_mode is 0 346 | if (attribute_proto->i != 0) { 347 | ERROR_F("non-zero ceil_mode is not supported for %s", 348 | node_proto->op_type); 349 | exit(1); 350 | } 351 | } else if (strcmp(attribute_proto->name, "storage_order") == 0) { 352 | ERROR_F("storage_order is not supported for %s", 353 | node_proto->op_type); 354 | exit(1); 355 | } else if (strcmp(attribute_proto->name, "dilations") == 0) { 356 | // ensure all dilations are 1, otherwise, panic 357 | for (size_t j = 1; j < attribute_proto->n_ints; j++) { 358 | if (attribute_proto->ints[j] != 1) { 359 | ERROR("All dilations must be 1 for max_pool_2d"); 360 | exit(1); 361 | } 362 | } 363 | } 364 | } 365 | } else { 366 | ERROR_F("Opset %d is not supported for %s", opset, node_proto->op_type); 367 | exit(1); 368 | } 369 | } 370 | 371 | MTDEF void mt_onnx__make_flatten(mt_layer *layer, int opset, 372 | Onnx__NodeProto *node_proto) { 373 | // Default values: 374 | layer->data.flatten.axis = 1; 375 | 376 | // Read attributes of flatten layer: axis 377 | if (opset >= 1 && opset <= 22) { 378 | for (size_t i = 0; i < node_proto->n_attribute; i++) { 379 | Onnx__AttributeProto *attribute_proto = node_proto->attribute[i]; 380 | if (strcmp(attribute_proto->name, "axis") == 0) { 381 | layer->data.flatten.axis = attribute_proto->i; 382 | } 383 | } 384 | } else { 385 | ERROR_F("Opset %d is not supported for %s", opset, node_proto->op_type); 386 | exit(1); 387 | } 388 | } 389 | 390 | MTDEF void mt_onnx__make_pad(mt_layer *layer, mt_model *model, int opset, 391 | Onnx__NodeProto *node_proto) { 392 | if (opset >= 2 && opset < 11) { 393 | // pads in the attribute 394 | MT_ASSERT_F(node_proto->n_input == 1, 395 | "Pad for opset %d must have 1 input", opset); 396 | 397 | // ensure `pads` attribute exists 398 | int found = 0; 399 | for (size_t i = 0; i < node_proto->n_attribute; i++) { 400 | if (strcmp(node_proto->attribute[i]->name, "pads") == 0) { 401 | found = 1; 402 | // ensure length of pads is 8 (double of input tensor's dims) 403 | if (node_proto->attribute[i]->n_ints != 8) { 404 | ERROR_F( 405 | "Supported Pad opset %d must have 8 pads, found %zu", 406 | opset, node_proto->attribute[i]->n_ints); 407 | exit(1); 408 | } 409 | 410 | /* To comply with mint's format, make pads as a layer input */ 411 | 412 | // assign a new tensor id for pads 413 | int pads_id = model->tensor_count; 414 | mt_float pads_values[8]; 415 | for (size_t j = 0; j < 8; j++) { 416 | pads_values[j] = node_proto->attribute[i]->ints[j]; 417 | } 418 | model->tensors[pads_id] = 419 | mt_tensor_alloc_values((int[]){8}, 1, pads_values); 420 | layer->inputs[1] = pads_id; 421 | model->tensor_count++; 422 | } 423 | } 424 | 425 | if (!found) { 426 | ERROR_F("Pad for opset %d must have `pads` attribute", opset); 427 | exit(1); 428 | } 429 | 430 | } else if (opset >= 11 && opset < 13) { 431 | // pads in the list of inputs 432 | ERROR_F("Pad is not supported yet for opset %d", opset); 433 | exit(1); 434 | } else { 435 | ERROR_F("Pad is not supported yet for opset %d", opset); 436 | exit(1); 437 | } 438 | } 439 | 440 | MTDEF void mt_onnx__make_instance_normalization(mt_layer *layer, int opset, 441 | Onnx__NodeProto *node_proto) { 442 | // Default values: 443 | layer->data.instance_normalization.eps = 1e-5; 444 | 445 | // Read attributes of instance_normalization layer: epsilon 446 | if (opset >= 1 && opset <= 22) { 447 | for (size_t i = 0; i < node_proto->n_attribute; i++) { 448 | Onnx__AttributeProto *attribute_proto = node_proto->attribute[i]; 449 | if (strcmp(attribute_proto->name, "epsilon") == 0) { 450 | layer->data.instance_normalization.eps = attribute_proto->f; 451 | } 452 | } 453 | } 454 | } 455 | 456 | MTDEF void mt_onnx__make_upsample(mt_layer *layer, mt_model *model, int opset, 457 | Onnx__NodeProto *node_proto) { 458 | // Default values: 459 | layer->data.upsample.mode = 0; 460 | 461 | // read `mode` attribute 462 | for (size_t i = 0; i < node_proto->n_attribute; i++) { 463 | if (strcmp(node_proto->attribute[i]->name, "mode") == 0) { 464 | if (strcmp((const char *)node_proto->attribute[i]->s.data, 465 | "nearest") == 0) { 466 | layer->data.upsample.mode = 0; 467 | } else if (strcmp((const char *)node_proto->attribute[i]->s.data, 468 | "bilinear") == 0) { 469 | layer->data.upsample.mode = 1; 470 | } else { 471 | ERROR_F("Unsupported Upsample mode: %s", 472 | node_proto->attribute[i]->s.data); 473 | } 474 | } 475 | } 476 | 477 | if (opset >= 7 && opset < 9) { 478 | // scale in the attribute, so ensure there is only one input 479 | MT_ASSERT_F(node_proto->n_input == 1, 480 | "Upsample for opset %d must have 1 input", opset); 481 | 482 | // ensure `scales` attribute exists 483 | for (size_t i = 0; i < node_proto->n_attribute; i++) { 484 | if (strcmp(node_proto->attribute[i]->name, "scales") == 0) { 485 | // ensure length of scales is 4 486 | if (node_proto->attribute[i]->n_floats != 4) { 487 | ERROR_F("Supported Upsample opset %d must have 4 scales, " 488 | "found %zu", 489 | opset, node_proto->attribute[i]->n_floats); 490 | } 491 | 492 | // set the scales as the layer input 493 | int scales_id = model->tensor_count; 494 | model->tensors[scales_id] = mt_tensor_alloc_values( 495 | (int[]){4}, 1, 496 | (mt_float[]){node_proto->attribute[i]->floats[0], 497 | node_proto->attribute[i]->floats[1], 498 | node_proto->attribute[i]->floats[2], 499 | node_proto->attribute[i]->floats[3]}); 500 | layer->inputs[1] = scales_id; 501 | model->tensor_count++; 502 | } 503 | } 504 | } else if (opset >= 9 && opset < 10) { 505 | // For now nothing to do for opset 9 since the scales are in the input 506 | } else { 507 | ERROR_F("Upsample is not supported yet for opset %d", opset); 508 | } 509 | } 510 | 511 | MTDEF mt_model *mt_onnx_read_mem(unsigned char *model_bytes, 512 | size_t model_bytes_len) { 513 | Onnx__ModelProto *model_proto = onnx__model_proto__unpack( 514 | NULL, model_bytes_len, (const uint8_t *)model_bytes); 515 | int opset = model_proto->opset_import[0]->version; 516 | 517 | DEBUG_LOG_F("Producer name : %s", model_proto->producer_name); 518 | DEBUG_LOG_F("Producer version : %s", model_proto->producer_version); 519 | DEBUG_LOG_F("Model version : %lld", model_proto->model_version); 520 | DEBUG_LOG_F("Opset : %d", opset); 521 | 522 | mt_model *model = (mt_model *)MT_MALLOC(sizeof(mt_model)); 523 | if (model == NULL) { 524 | ERROR("Failed to allocate memory for model"); 525 | return NULL; 526 | } 527 | model->layer_count = model_proto->graph->n_node; 528 | model->input_count = model_proto->graph->n_input; 529 | model->output_count = model_proto->graph->n_output; 530 | 531 | // First we collect all ONNX tensors and their names without assigning 532 | // final IDs 533 | 534 | // Track all tensors in the model 535 | typedef struct { 536 | char *name; 537 | mt_tensor *data; // NULL for non-initializers 538 | int is_input; 539 | int is_output; 540 | int is_initializer; 541 | } tensor_map_t; 542 | 543 | tensor_map_t all_tensors[MAX_MODEL_INITIALIZER_COUNT]; 544 | int total_tensors = 0; 545 | 546 | // Initialize name-to-index mapping for quick lookups 547 | char *tensor_names[MAX_MODEL_INITIALIZER_COUNT]; 548 | int tensor_count = 0; 549 | 550 | // First, collect all model inputs 551 | for (size_t i = 0; i < model_proto->graph->n_input; i++) { 552 | Onnx__ValueInfoProto *value_info_proto = model_proto->graph->input[i]; 553 | all_tensors[total_tensors].name = value_info_proto->name; 554 | all_tensors[total_tensors].data = NULL; 555 | all_tensors[total_tensors].is_input = 1; 556 | all_tensors[total_tensors].is_output = 0; 557 | all_tensors[total_tensors].is_initializer = 0; 558 | tensor_names[tensor_count++] = value_info_proto->name; 559 | total_tensors++; 560 | } 561 | 562 | // Next, collect all initializers (weights) 563 | for (size_t i = 0; i < model_proto->graph->n_initializer; i++) { 564 | Onnx__TensorProto *tensor_proto = model_proto->graph->initializer[i]; 565 | 566 | // Check if this initializer is already in our list (as an input) 567 | int found = 0; 568 | for (int j = 0; j < total_tensors; j++) { 569 | if (strcmp(all_tensors[j].name, tensor_proto->name) == 0) { 570 | // This is both an input and initializer 571 | all_tensors[j].data = 572 | mt_onnx__tensor_proto_to_mt_tensor(tensor_proto); 573 | all_tensors[j].is_initializer = 1; 574 | found = 1; 575 | break; 576 | } 577 | } 578 | 579 | if (!found) { 580 | all_tensors[total_tensors].name = tensor_proto->name; 581 | all_tensors[total_tensors].data = 582 | mt_onnx__tensor_proto_to_mt_tensor(tensor_proto); 583 | all_tensors[total_tensors].is_input = 0; 584 | all_tensors[total_tensors].is_output = 0; 585 | all_tensors[total_tensors].is_initializer = 1; 586 | tensor_names[tensor_count++] = tensor_proto->name; 587 | total_tensors++; 588 | } 589 | } 590 | 591 | // Next, collect all outputs 592 | for (size_t i = 0; i < model_proto->graph->n_output; i++) { 593 | Onnx__ValueInfoProto *value_info_proto = model_proto->graph->output[i]; 594 | 595 | // Check if this output is already in our list 596 | int found = 0; 597 | for (int j = 0; j < total_tensors; j++) { 598 | if (strcmp(all_tensors[j].name, value_info_proto->name) == 0) { 599 | all_tensors[j].is_output = 1; 600 | found = 1; 601 | break; 602 | } 603 | } 604 | 605 | if (!found) { 606 | all_tensors[total_tensors].name = value_info_proto->name; 607 | all_tensors[total_tensors].data = NULL; 608 | all_tensors[total_tensors].is_input = 0; 609 | all_tensors[total_tensors].is_output = 1; 610 | all_tensors[total_tensors].is_initializer = 0; 611 | tensor_names[tensor_count++] = value_info_proto->name; 612 | total_tensors++; 613 | } 614 | } 615 | 616 | // Collect node inputs/outputs 617 | for (size_t i = 0; i < model_proto->graph->n_node; i++) { 618 | Onnx__NodeProto *node = model_proto->graph->node[i]; 619 | 620 | for (size_t j = 0; j < node->n_input; j++) { 621 | char *input_name = node->input[j]; 622 | 623 | // Check if already in the list 624 | int found = 0; 625 | for (int k = 0; k < tensor_count; k++) { 626 | if (strcmp(tensor_names[k], input_name) == 0) { 627 | found = 1; 628 | break; 629 | } 630 | } 631 | 632 | if (!found) { 633 | tensor_names[tensor_count++] = input_name; 634 | 635 | // Add to all_tensors 636 | all_tensors[total_tensors].name = input_name; 637 | all_tensors[total_tensors].data = NULL; 638 | all_tensors[total_tensors].is_input = 0; 639 | all_tensors[total_tensors].is_output = 0; 640 | all_tensors[total_tensors].is_initializer = 0; 641 | total_tensors++; 642 | } 643 | } 644 | 645 | for (size_t j = 0; j < node->n_output; j++) { 646 | char *output_name = node->output[j]; 647 | 648 | // Check if already in the list 649 | int found = 0; 650 | for (int k = 0; k < tensor_count; k++) { 651 | if (strcmp(tensor_names[k], output_name) == 0) { 652 | found = 1; 653 | break; 654 | } 655 | } 656 | 657 | if (!found) { 658 | tensor_names[tensor_count++] = output_name; 659 | 660 | // Add to all_tensors 661 | all_tensors[total_tensors].name = output_name; 662 | all_tensors[total_tensors].data = NULL; 663 | all_tensors[total_tensors].is_input = 0; 664 | all_tensors[total_tensors].is_output = 0; 665 | all_tensors[total_tensors].is_initializer = 0; 666 | total_tensors++; 667 | } 668 | } 669 | } 670 | 671 | // Now remap tensors to match dump.py's pattern 672 | // First inputs 673 | int tensor_idx = 0; 674 | int input_idx = 0; 675 | int output_idx = 0; 676 | tensor_info_t tensor_infos[MAX_MODEL_INITIALIZER_COUNT]; 677 | 678 | // Map inputs first, matching dump.py approach 679 | for (int i = 0; i < total_tensors; i++) { 680 | if (all_tensors[i].is_input) { 681 | tensor_infos[tensor_idx].id = tensor_idx; 682 | tensor_infos[tensor_idx].name = all_tensors[i].name; 683 | model->tensors[tensor_idx] = all_tensors[i].data; 684 | 685 | model->inputs[input_idx].id = tensor_idx; 686 | strncpy(model->inputs[input_idx].name, all_tensors[i].name, 687 | MAX_INPUT_OUTPUT_NAME_LEN); 688 | strncpy(model->tensor_names[tensor_idx], all_tensors[i].name, 689 | MAX_INPUT_OUTPUT_NAME_LEN); 690 | 691 | input_idx++; 692 | tensor_idx++; 693 | } 694 | } 695 | 696 | // Map initializers next (weights with data), exactly like dump.py 697 | for (int i = 0; i < total_tensors; i++) { 698 | if (all_tensors[i].is_initializer && !all_tensors[i].is_input) { 699 | tensor_infos[tensor_idx].id = tensor_idx; 700 | tensor_infos[tensor_idx].name = all_tensors[i].name; 701 | model->tensors[tensor_idx] = all_tensors[i].data; 702 | strncpy(model->tensor_names[tensor_idx], all_tensors[i].name, 703 | MAX_INPUT_OUTPUT_NAME_LEN); 704 | tensor_idx++; 705 | } 706 | } 707 | 708 | // Map outputs 709 | for (int i = 0; i < total_tensors; i++) { 710 | if (all_tensors[i].is_output && !all_tensors[i].is_input && 711 | !all_tensors[i].is_initializer) { 712 | tensor_infos[tensor_idx].id = tensor_idx; 713 | tensor_infos[tensor_idx].name = all_tensors[i].name; 714 | model->tensors[tensor_idx] = NULL; 715 | 716 | model->outputs[output_idx].id = tensor_idx; 717 | strncpy(model->outputs[output_idx].name, all_tensors[i].name, 718 | MAX_INPUT_OUTPUT_NAME_LEN); 719 | strncpy(model->tensor_names[tensor_idx], all_tensors[i].name, 720 | MAX_INPUT_OUTPUT_NAME_LEN); 721 | 722 | output_idx++; 723 | tensor_idx++; 724 | } 725 | } 726 | 727 | // Map remaining intermediate tensors 728 | for (int i = 0; i < total_tensors; i++) { 729 | if (!all_tensors[i].is_input && !all_tensors[i].is_initializer && 730 | !all_tensors[i].is_output) { 731 | tensor_infos[tensor_idx].id = tensor_idx; 732 | tensor_infos[tensor_idx].name = all_tensors[i].name; 733 | model->tensors[tensor_idx] = NULL; 734 | strncpy(model->tensor_names[tensor_idx], all_tensors[i].name, 735 | MAX_INPUT_OUTPUT_NAME_LEN); 736 | tensor_idx++; 737 | } 738 | } 739 | 740 | model->tensor_count = tensor_count; 741 | 742 | // Collect node info, including name, input names, output names, input 743 | // count 744 | node_info_t node_infos[MAX_LAYER_COUNT]; 745 | for (size_t i = 0; i < model_proto->graph->n_node; i++) { 746 | Onnx__NodeProto *node_proto = model_proto->graph->node[i]; 747 | node_infos[i].id = i; 748 | node_infos[i].input_count = node_proto->n_input; 749 | node_infos[i].output_count = node_proto->n_output; 750 | node_infos[i].name = node_proto->name; 751 | for (size_t j = 0; j < node_proto->n_input; j++) { 752 | node_infos[i].input_names[j] = node_proto->input[j]; 753 | } 754 | for (size_t j = 0; j < node_proto->n_output; j++) { 755 | node_infos[i].output_names[j] = node_proto->output[j]; 756 | } 757 | } 758 | 759 | // Read layers (nodes) 760 | Onnx__NodeProto **nodes = model_proto->graph->node; 761 | DEBUG_LOG_F("Nodes: %zu", model_proto->graph->n_node); 762 | for (size_t i = 0; i < model_proto->graph->n_node; i++) { 763 | Onnx__NodeProto *node_proto = nodes[i]; 764 | 765 | mt_layer *layer = (mt_layer *)MT_MALLOC(sizeof(mt_layer)); 766 | mt_layer_kind kind = mt_onnx__get_layer_kind(node_proto->op_type); 767 | layer->kind = kind; 768 | layer->input_count = node_proto->n_input; 769 | layer->output_count = node_proto->n_output; 770 | layer->id = i; 771 | model->layers[i] = layer; 772 | 773 | for (size_t j = 0; j < node_proto->n_input; j++) { 774 | int input_id = mt_onnx__get_tensor_id(tensor_infos, tensor_idx, 775 | node_proto->input[j]); 776 | if (input_id == -1) { 777 | ERROR_F("Input %s not found", node_proto->input[j]); 778 | } 779 | layer->inputs[j] = input_id; 780 | } 781 | 782 | for (size_t j = 0; j < node_proto->n_output; j++) { 783 | int output_id = mt_onnx__get_tensor_id(tensor_infos, tensor_idx, 784 | node_proto->output[j]); 785 | if (output_id == -1) { 786 | ERROR_F("Output %s not found", node_proto->output[j]); 787 | } 788 | layer->outputs[j] = output_id; 789 | } 790 | 791 | switch (kind) { 792 | case MT_LAYER_CONV_2D: 793 | mt_onnx__make_conv(layer, opset, node_proto); 794 | break; 795 | case MT_LAYER_MAX_POOL_2D: 796 | mt_onnx__make_max_pool(layer, opset, node_proto); 797 | break; 798 | case MT_LAYER_FLATTEN: 799 | mt_onnx__make_flatten(layer, opset, node_proto); 800 | break; 801 | case MT_LAYER_DENSE: 802 | mt_onnx__make_dense(model, layer, opset, node_proto); 803 | break; 804 | case MT_LAYER_PAD: 805 | mt_onnx__make_pad(layer, model, opset, node_proto); 806 | break; 807 | case MT_LAYER_INSTANCE_NORMALIZATION: 808 | mt_onnx__make_instance_normalization(layer, opset, node_proto); 809 | break; 810 | case MT_LAYER_UPSAMPLE: 811 | mt_onnx__make_upsample(layer, model, opset, node_proto); 812 | break; 813 | // Pass through, since there's no data to parse 814 | case MT_LAYER_ADD: 815 | case MT_LAYER_EXP: 816 | case MT_LAYER_RELU: 817 | case MT_LAYER_GLOBAL_AVG_POOL: 818 | case MT_LAYER_CONSTANT: 819 | break; 820 | default: 821 | ERROR_F("layer kind %s is not supported", node_proto->op_type); 822 | } 823 | 824 | // Find previous nodes that connect to this node 825 | for (size_t j = 0; j < i; j++) { 826 | node_info_t prev_node_info = node_infos[j]; 827 | for (size_t k = 0; k < (size_t)prev_node_info.output_count; k++) { 828 | char *output_name = prev_node_info.output_names[k]; 829 | for (size_t l = 0; l < node_proto->n_input; l++) { 830 | if (strcmp(output_name, node_proto->input[l]) == 0) { 831 | // Record the connection in the model's layer 832 | // structure 833 | if (layer->prev_count < MAX_LAYER_PREV_COUNT) { 834 | layer->prev[layer->prev_count++] = j; 835 | 836 | // Also update the next connection for the 837 | // previous layer 838 | mt_layer *prev_layer = model->layers[j]; 839 | if (prev_layer->next_count < MAX_LAYER_NEXT_COUNT) { 840 | prev_layer->next[prev_layer->next_count++] = i; 841 | } 842 | } 843 | // Once we've found a connection for this input, 844 | // move to the next input 845 | break; 846 | } 847 | } 848 | } 849 | } 850 | } 851 | 852 | onnx__model_proto__free_unpacked(model_proto, NULL); 853 | return model; 854 | } 855 | #endif 856 | 857 | #endif // MINT_ONNX_H 858 | -------------------------------------------------------------------------------- /onnx/README.md: -------------------------------------------------------------------------------- 1 | ```bash 2 | $ protoc --c_out=. onnx.proto3 3 | $ sed -i '' 's|#include |#include "protobuf-c.h"|' onnx.proto3.pb-c.h 4 | ``` -------------------------------------------------------------------------------- /onnx/onnx.proto3: -------------------------------------------------------------------------------- 1 | // 2 | // WARNING: This file is automatically generated! Please edit onnx.in.proto. 3 | // 4 | 5 | 6 | // SPDX-License-Identifier: Apache-2.0 7 | 8 | 9 | syntax = "proto3"; 10 | 11 | package onnx; 12 | 13 | // Overview 14 | // 15 | // ONNX is an open specification that is comprised of the following components: 16 | // 17 | // 1) A definition of an extensible computation graph model. 18 | // 2) Definitions of standard data types. 19 | // 3) Definitions of built-in operators. 20 | // 21 | // This document describes the syntax of models and their computation graphs, 22 | // as well as the standard data types. Together, they are referred to as the ONNX 23 | // Intermediate Representation, or 'IR' for short. 24 | // 25 | // The normative semantic specification of the ONNX IR is found in docs/IR.md. 26 | // Definitions of the built-in neural network operators may be found in docs/Operators.md. 27 | 28 | // Notes 29 | // 30 | // Protobuf compatibility 31 | // 32 | // To simplify framework compatibility, ONNX is defined using the subset of protobuf 33 | // that is compatible with both protobuf v2 and v3. This means that we do not use any 34 | // protobuf features that are only available in one of the two versions. 35 | // 36 | // Here are the most notable contortions we have to carry out to work around 37 | // these limitations: 38 | // 39 | // - No 'map' (added protobuf 3.0). We instead represent mappings as lists 40 | // of key-value pairs, where order does not matter and duplicates 41 | // are not allowed. 42 | 43 | 44 | // Versioning 45 | // 46 | // ONNX versioning is specified in docs/IR.md and elaborated on in docs/Versioning.md 47 | // 48 | // To be compatible with both proto2 and proto3, we will use a version number 49 | // that is not defined by the default value but an explicit enum number. 50 | enum Version { 51 | // proto3 requires the first enum value to be zero. 52 | // We add this just to appease the compiler. 53 | _START_VERSION = 0; 54 | // The version field is always serialized and we will use it to store the 55 | // version that the graph is generated from. This helps us set up version 56 | // control. 57 | // For the IR, we are using simple numbers starting with 0x00000001, 58 | // which was the version we published on Oct 10, 2017. 59 | IR_VERSION_2017_10_10 = 0x0000000000000001; 60 | 61 | // IR_VERSION 2 published on Oct 30, 2017 62 | // - Added type discriminator to AttributeProto to support proto3 users 63 | IR_VERSION_2017_10_30 = 0x0000000000000002; 64 | 65 | // IR VERSION 3 published on Nov 3, 2017 66 | // - For operator versioning: 67 | // - Added new message OperatorSetIdProto 68 | // - Added opset_import in ModelProto 69 | // - For vendor extensions, added domain in NodeProto 70 | IR_VERSION_2017_11_3 = 0x0000000000000003; 71 | 72 | // IR VERSION 4 published on Jan 22, 2019 73 | // - Relax constraint that initializers should be a subset of graph inputs 74 | // - Add type BFLOAT16 75 | IR_VERSION_2019_1_22 = 0x0000000000000004; 76 | 77 | // IR VERSION 5 published on March 18, 2019 78 | // - Add message TensorAnnotation. 79 | // - Add quantization annotation in GraphProto to map tensor with its scale and zero point quantization parameters. 80 | IR_VERSION_2019_3_18 = 0x0000000000000005; 81 | 82 | // IR VERSION 6 published on Sep 19, 2019 83 | // - Add support for sparse tensor constants stored in model. 84 | // - Add message SparseTensorProto 85 | // - Add sparse initializers 86 | IR_VERSION_2019_9_19 = 0x0000000000000006; 87 | 88 | // IR VERSION 7 published on May 8, 2020 89 | // - Add support to allow function body graph to rely on multiple external operator sets. 90 | // - Add a list to promote inference graph's initializers to global and 91 | // mutable variables. Global variables are visible in all graphs of the 92 | // stored models. 93 | // - Add message TrainingInfoProto to store initialization 94 | // method and training algorithm. The execution of TrainingInfoProto 95 | // can modify the values of mutable variables. 96 | // - Implicitly add inference graph into each TrainingInfoProto's algorithm. 97 | IR_VERSION_2020_5_8 = 0x0000000000000007; 98 | 99 | // IR VERSION 8 published on July 30, 2021 100 | // Introduce TypeProto.SparseTensor 101 | // Introduce TypeProto.Optional 102 | // Added a list of FunctionProtos local to the model 103 | // Deprecated since_version and operator status from FunctionProto 104 | IR_VERSION_2021_7_30 = 0x0000000000000008; 105 | 106 | // IR VERSION 9 published on May 5, 2023 107 | // Added AttributeProto to FunctionProto so that default attribute values can be set. 108 | // Added FLOAT8E4M3FN, FLOAT8E4M3FNUZ, FLOAT8E5M2, FLOAT8E5M2FNUZ. 109 | IR_VERSION_2023_5_5 = 0x0000000000000009; 110 | 111 | // IR VERSION 10 published on March 25, 2024 112 | // Added UINT4, INT4, overload field for functions and metadata_props on multiple proto definitions. 113 | IR_VERSION_2024_3_25 = 0x000000000000000A; 114 | 115 | // IR VERSION 11 published on May 12, 2025 116 | // Added FLOAT4E2M1, multi-device protobuf classes. 117 | IR_VERSION = 0x000000000000000B; 118 | } 119 | 120 | // Attributes 121 | // 122 | // A named attribute containing either singular float, integer, string, graph, 123 | // and tensor values, or repeated float, integer, string, graph, and tensor values. 124 | // An AttributeProto MUST contain the name field, and *only one* of the 125 | // following content fields, effectively enforcing a C/C++ union equivalent. 126 | message AttributeProto { 127 | reserved 12, 16 to 19; 128 | reserved "v"; 129 | 130 | // Note: this enum is structurally identical to the OpSchema::AttrType 131 | // enum defined in schema.h. If you rev one, you likely need to rev the other. 132 | enum AttributeType { 133 | UNDEFINED = 0; 134 | FLOAT = 1; 135 | INT = 2; 136 | STRING = 3; 137 | TENSOR = 4; 138 | GRAPH = 5; 139 | SPARSE_TENSOR = 11; 140 | TYPE_PROTO = 13; 141 | 142 | FLOATS = 6; 143 | INTS = 7; 144 | STRINGS = 8; 145 | TENSORS = 9; 146 | GRAPHS = 10; 147 | SPARSE_TENSORS = 12; 148 | TYPE_PROTOS = 14; 149 | } 150 | 151 | // The name field MUST be present for this version of the IR. 152 | string name = 1; // namespace Attribute 153 | 154 | // if ref_attr_name is not empty, ref_attr_name is the attribute name in parent function. 155 | // In this case, this AttributeProto does not contain data, and it's a reference of attribute 156 | // in parent scope. 157 | // NOTE: This should ONLY be used in function (sub-graph). It's invalid to be used in main graph. 158 | string ref_attr_name = 21; 159 | 160 | // A human-readable documentation for this attribute. Markdown is allowed. 161 | string doc_string = 13; 162 | 163 | // The type field MUST be present for this version of the IR. 164 | // For 0.0.1 versions of the IR, this field was not defined, and 165 | // implementations needed to use has_field heuristics to determine 166 | // which value field was in use. For IR_VERSION 0.0.2 or later, this 167 | // field MUST be set and match the f|i|s|t|... field in use. This 168 | // change was made to accommodate proto3 implementations. 169 | AttributeType type = 20; // discriminator that indicates which field below is in use 170 | 171 | // Exactly ONE of the following fields must be present for this version of the IR 172 | float f = 2; // float 173 | int64 i = 3; // int 174 | bytes s = 4; // UTF-8 string 175 | TensorProto t = 5; // tensor value 176 | GraphProto g = 6; // graph 177 | SparseTensorProto sparse_tensor = 22; // sparse tensor value 178 | // Do not use field below, it's deprecated. 179 | // optional ValueProto v = 12; // value - subsumes everything but graph 180 | TypeProto tp = 14; // type proto 181 | 182 | repeated float floats = 7; // list of floats 183 | repeated int64 ints = 8; // list of ints 184 | repeated bytes strings = 9; // list of UTF-8 strings 185 | repeated TensorProto tensors = 10; // list of tensors 186 | repeated GraphProto graphs = 11; // list of graph 187 | repeated SparseTensorProto sparse_tensors = 23; // list of sparse tensors 188 | repeated TypeProto type_protos = 15;// list of type protos 189 | } 190 | 191 | // Defines information on value, including the name, the type, and 192 | // the shape of the value. 193 | message ValueInfoProto { 194 | // This field MUST be present in this version of the IR. 195 | string name = 1; // namespace Value 196 | // This field MUST be present in this version of the IR for 197 | // inputs and outputs of the top-level graph. 198 | TypeProto type = 2; 199 | // A human-readable documentation for this value. Markdown is allowed. 200 | string doc_string = 3; 201 | // Named metadata values; keys should be distinct. 202 | repeated StringStringEntryProto metadata_props = 4; 203 | } 204 | 205 | // Nodes 206 | // 207 | // Computation graphs are made up of a DAG of nodes, which represent what is 208 | // commonly called a "layer" or "pipeline stage" in machine learning frameworks. 209 | // 210 | // For example, it can be a node of type "Conv" that takes in an image, a filter 211 | // tensor and a bias tensor, and produces the convolved output. 212 | message NodeProto { 213 | repeated string input = 1; // namespace Value 214 | repeated string output = 2; // namespace Value 215 | 216 | // An optional identifier for this node in a graph. 217 | // This field MAY be absent in this version of the IR. 218 | string name = 3; // namespace Node 219 | 220 | // The symbolic identifier of the Operator to execute. 221 | string op_type = 4; // namespace Operator 222 | // The domain of the OperatorSet that specifies the operator named by op_type. 223 | string domain = 7; // namespace Domain 224 | // Overload identifier, used only to map this to a model-local function. 225 | string overload = 8; 226 | 227 | // Additional named attributes. 228 | repeated AttributeProto attribute = 5; 229 | 230 | // A human-readable documentation for this node. Markdown is allowed. 231 | string doc_string = 6; 232 | 233 | // Named metadata values; keys should be distinct. 234 | repeated StringStringEntryProto metadata_props = 9; 235 | 236 | // Configuration of multi-device annotations. 237 | repeated NodeDeviceConfigurationProto device_configurations = 10; 238 | } 239 | 240 | // IntIntListEntryProto follows the pattern for cross-proto-version maps. 241 | // See https://developers.google.com/protocol-buffers/docs/proto3#maps 242 | message IntIntListEntryProto { 243 | int64 key = 1; 244 | repeated int64 value = 2; 245 | }; 246 | 247 | // Multi-device configuration proto for NodeProto. 248 | message NodeDeviceConfigurationProto { 249 | // This field MUST be present for this version of the IR. 250 | // ID of the configuration. MUST match the name of a DeviceConfigurationProto. 251 | string configuration_id = 1; 252 | // Sharding spec for the node. 253 | repeated ShardingSpecProto sharding_spec = 2; 254 | // Pipeline stage of this node. 255 | int32 pipeline_stage = 3; 256 | } 257 | 258 | // ShardingSpecProto: This describes the sharding spec for a specific 259 | // input or output tensor of a node. 260 | message ShardingSpecProto { 261 | // This field MUST be present for this version of the IR. 262 | // Identifies the input or output of the node that is being sharded. 263 | // Required to match a name specified in the node's input or output list of ValueInfoProtos. 264 | // It is called `logical tensor` in subsequent descriptions. 265 | string tensor_name = 1; 266 | 267 | // The following is the list of devices across which the logical 268 | // tensor is sharded or replicated. 269 | repeated int64 device = 2; 270 | 271 | // Each element v in above field devices may represent either a 272 | // device or a set of devices (when we want the same shard/tensor 273 | // to be replicated across a subset of devices), as indicated by 274 | // the following optional map. If the map contains an entry for v, 275 | // then v represents a device group, and the map indicates the set 276 | // of devices in that group. 277 | repeated IntIntListEntryProto index_to_device_group_map = 3; 278 | 279 | // The following is the sharded-shape of the tensor, consisting of 280 | // the sharding-spec for each axis of the tensor. 281 | repeated ShardedDimProto sharded_dim = 4; 282 | } 283 | 284 | // ShardedDimProto: This describes the sharding spec for a single 285 | // axis of a sharded tensor. 286 | message ShardedDimProto { 287 | // This field MUST be present for this version of the IR. 288 | // The axis this sharding corresponds to. Must be in the range of 289 | // [-r, r - 1], where r is the rank of the tensor. Negative axis values means 290 | // counting from the back. 291 | int64 axis = 1; 292 | 293 | // Describes how the tensor on the provided axis is sharded. 294 | // The common-case is described by a single instance of SimpleShardedDimProto. 295 | // Multiple instances can be used to handle cases where a sharded 296 | // tensor is reshaped, fusing multiple axes into one. 297 | repeated SimpleShardedDimProto simple_sharding = 2; 298 | } 299 | 300 | // SimpleShardedDimProto: Indicates that N blocks are divided into M shards. 301 | // N is allowed to be symbolic where M is required to be a constant. 302 | message SimpleShardedDimProto { 303 | // Dimension value to be sharded. 304 | oneof dim { 305 | int64 dim_value = 1; 306 | string dim_param = 2; 307 | } 308 | 309 | // This field MUST be present for this version of the IR. 310 | // Number of shards to split dim into. 311 | int64 num_shards = 3; 312 | } 313 | 314 | // Training information 315 | // TrainingInfoProto stores information for training a model. 316 | // In particular, this defines two functionalities: an initialization-step 317 | // and a training-algorithm-step. Initialization resets the model 318 | // back to its original state as if no training has been performed. 319 | // Training algorithm improves the model based on input data. 320 | // 321 | // The semantics of the initialization-step is that the initializers 322 | // in ModelProto.graph and in TrainingInfoProto.algorithm are first 323 | // initialized as specified by the initializers in the graph, and then 324 | // updated by the "initialization_binding" in every instance in 325 | // ModelProto.training_info. 326 | // 327 | // The field "algorithm" defines a computation graph which represents a 328 | // training algorithm's step. After the execution of a 329 | // TrainingInfoProto.algorithm, the initializers specified by "update_binding" 330 | // may be immediately updated. If the targeted training algorithm contains 331 | // consecutive update steps (such as block coordinate descent methods), 332 | // the user needs to create a TrainingInfoProto for each step. 333 | message TrainingInfoProto { 334 | // This field describes a graph to compute the initial tensors 335 | // upon starting the training process. Initialization graph has no input 336 | // and can have multiple outputs. Usually, trainable tensors in neural 337 | // networks are randomly initialized. To achieve that, for each tensor, 338 | // the user can put a random number operator such as RandomNormal or 339 | // RandomUniform in TrainingInfoProto.initialization.node and assign its 340 | // random output to the specific tensor using "initialization_binding". 341 | // This graph can also set the initializers in "algorithm" in the same 342 | // TrainingInfoProto; a use case is resetting the number of training 343 | // iteration to zero. 344 | // 345 | // By default, this field is an empty graph and its evaluation does not 346 | // produce any output. Thus, no initializer would be changed by default. 347 | GraphProto initialization = 1; 348 | 349 | // This field represents a training algorithm step. Given required inputs, 350 | // it computes outputs to update initializers in its own or inference graph's 351 | // initializer lists. In general, this field contains loss node, gradient node, 352 | // optimizer node, increment of iteration count. 353 | // 354 | // An execution of the training algorithm step is performed by executing the 355 | // graph obtained by combining the inference graph (namely "ModelProto.graph") 356 | // and the "algorithm" graph. That is, the actual 357 | // input/initializer/output/node/value_info/sparse_initializer list of 358 | // the training graph is the concatenation of 359 | // "ModelProto.graph.input/initializer/output/node/value_info/sparse_initializer" 360 | // and "algorithm.input/initializer/output/node/value_info/sparse_initializer" 361 | // in that order. This combined graph must satisfy the normal ONNX conditions. 362 | // Now, let's provide a visualization of graph combination for clarity. 363 | // Let the inference graph (i.e., "ModelProto.graph") be 364 | // tensor_a, tensor_b -> MatMul -> tensor_c -> Sigmoid -> tensor_d 365 | // and the "algorithm" graph be 366 | // tensor_d -> Add -> tensor_e 367 | // The combination process results 368 | // tensor_a, tensor_b -> MatMul -> tensor_c -> Sigmoid -> tensor_d -> Add -> tensor_e 369 | // 370 | // Notice that an input of a node in the "algorithm" graph may reference the 371 | // output of a node in the inference graph (but not the other way round). Also, inference 372 | // node cannot reference inputs of "algorithm". With these restrictions, inference graph 373 | // can always be run independently without training information. 374 | // 375 | // By default, this field is an empty graph and its evaluation does not 376 | // produce any output. Evaluating the default training step never 377 | // update any initializers. 378 | GraphProto algorithm = 2; 379 | 380 | // This field specifies the bindings from the outputs of "initialization" to 381 | // some initializers in "ModelProto.graph.initializer" and 382 | // the "algorithm.initializer" in the same TrainingInfoProto. 383 | // See "update_binding" below for details. 384 | // 385 | // By default, this field is empty and no initializer would be changed 386 | // by the execution of "initialization". 387 | repeated StringStringEntryProto initialization_binding = 3; 388 | 389 | // Gradient-based training is usually an iterative procedure. In one gradient 390 | // descent iteration, we apply 391 | // 392 | // x = x - r * g 393 | // 394 | // where "x" is the optimized tensor, "r" stands for learning rate, and "g" is 395 | // gradient of "x" with respect to a chosen loss. To avoid adding assignments 396 | // into the training graph, we split the update equation into 397 | // 398 | // y = x - r * g 399 | // x = y 400 | // 401 | // The user needs to save "y = x - r * g" into TrainingInfoProto.algorithm. To 402 | // tell that "y" should be assigned to "x", the field "update_binding" may 403 | // contain a key-value pair of strings, "x" (key of StringStringEntryProto) 404 | // and "y" (value of StringStringEntryProto). 405 | // For a neural network with multiple trainable (mutable) tensors, there can 406 | // be multiple key-value pairs in "update_binding". 407 | // 408 | // The initializers appears as keys in "update_binding" are considered 409 | // mutable variables. This implies some behaviors 410 | // as described below. 411 | // 412 | // 1. We have only unique keys in all "update_binding"s so that two 413 | // variables may not have the same name. This ensures that one 414 | // variable is assigned up to once. 415 | // 2. The keys must appear in names of "ModelProto.graph.initializer" or 416 | // "TrainingInfoProto.algorithm.initializer". 417 | // 3. The values must be output names of "algorithm" or "ModelProto.graph.output". 418 | // 4. Mutable variables are initialized to the value specified by the 419 | // corresponding initializer, and then potentially updated by 420 | // "initializer_binding"s and "update_binding"s in "TrainingInfoProto"s. 421 | // 422 | // This field usually contains names of trainable tensors 423 | // (in ModelProto.graph), optimizer states such as momentums in advanced 424 | // stochastic gradient methods (in TrainingInfoProto.graph), 425 | // and number of training iterations (in TrainingInfoProto.graph). 426 | // 427 | // By default, this field is empty and no initializer would be changed 428 | // by the execution of "algorithm". 429 | repeated StringStringEntryProto update_binding = 4; 430 | } 431 | 432 | // Models 433 | // 434 | // ModelProto is a top-level file/container format for bundling a ML model and 435 | // associating its computation graph with metadata. 436 | // 437 | // The semantics of the model are described by the associated GraphProto's. 438 | message ModelProto { 439 | // The version of the IR this model targets. See Version enum above. 440 | // This field MUST be present. 441 | int64 ir_version = 1; 442 | 443 | // The OperatorSets this model relies on. 444 | // All ModelProtos MUST have at least one entry that 445 | // specifies which version of the ONNX OperatorSet is 446 | // being imported. 447 | // 448 | // All nodes in the ModelProto's graph will bind against the operator 449 | // with the same-domain/same-op_type operator with the HIGHEST version 450 | // in the referenced operator sets. 451 | repeated OperatorSetIdProto opset_import = 8; 452 | 453 | // The name of the framework or tool used to generate this model. 454 | // This field SHOULD be present to indicate which implementation/tool/framework 455 | // emitted the model. 456 | string producer_name = 2; 457 | 458 | // The version of the framework or tool used to generate this model. 459 | // This field SHOULD be present to indicate which implementation/tool/framework 460 | // emitted the model. 461 | string producer_version = 3; 462 | 463 | // Domain name of the model. 464 | // We use reverse domain names as name space indicators. For example: 465 | // `com.facebook.fair` or `com.microsoft.cognitiveservices` 466 | // 467 | // Together with `model_version` and GraphProto.name, this forms the unique identity of 468 | // the graph. 469 | string domain = 4; 470 | 471 | // The version of the graph encoded. See Version enum below. 472 | int64 model_version = 5; 473 | 474 | // A human-readable documentation for this model. Markdown is allowed. 475 | string doc_string = 6; 476 | 477 | // The parameterized graph that is evaluated to execute the model. 478 | GraphProto graph = 7; 479 | 480 | // Named metadata values; keys should be distinct. 481 | repeated StringStringEntryProto metadata_props = 14; 482 | 483 | // Training-specific information. Sequentially executing all stored 484 | // `TrainingInfoProto.algorithm`s and assigning their outputs following 485 | // the corresponding `TrainingInfoProto.update_binding`s is one training 486 | // iteration. Similarly, to initialize the model 487 | // (as if training hasn't happened), the user should sequentially execute 488 | // all stored `TrainingInfoProto.initialization`s and assigns their outputs 489 | // using `TrainingInfoProto.initialization_binding`s. 490 | // 491 | // If this field is empty, the training behavior of the model is undefined. 492 | repeated TrainingInfoProto training_info = 20; 493 | 494 | // A list of function protos local to the model. 495 | // 496 | // The (domain, name, overload) tuple must be unique across the function protos in this list. 497 | // In case of any conflicts the behavior (whether the model local functions are given higher priority, 498 | // or standard operator sets are given higher priority or this is treated as error) is defined by 499 | // the runtimes. 500 | // 501 | // The operator sets imported by FunctionProto should be compatible with the ones 502 | // imported by ModelProto and other model local FunctionProtos. 503 | // Example, if same operator set say 'A' is imported by a FunctionProto and ModelProto 504 | // or by 2 FunctionProtos then versions for the operator set may be different but, 505 | // the operator schema returned for op_type, domain, version combination 506 | // for both the versions should be same for every node in the function body. 507 | // 508 | // One FunctionProto can reference other FunctionProto in the model, however, recursive reference 509 | // is not allowed. 510 | repeated FunctionProto functions = 25; 511 | 512 | // Describes different target configurations for a multi-device use case. 513 | // A model MAY describe multiple multi-device configurations for execution. 514 | repeated DeviceConfigurationProto configuration = 26; 515 | }; 516 | 517 | // DeviceConfigurationProto describes a multi-device configuration for a model. 518 | message DeviceConfigurationProto { 519 | // This field MUST be present for this version of the IR. 520 | // Name of the configuration. 521 | string name = 1; 522 | // This field MUST be present for this version of the IR. 523 | // Number of devices inside this configuration. 524 | int32 num_devices = 2; 525 | // Optional names of the devices. MUST be length of num_devices if provided. 526 | repeated string device = 3; 527 | } 528 | 529 | // StringStringEntryProto follows the pattern for cross-proto-version maps. 530 | // See https://developers.google.com/protocol-buffers/docs/proto3#maps 531 | message StringStringEntryProto { 532 | string key = 1; 533 | string value = 2; 534 | }; 535 | 536 | message TensorAnnotation { 537 | string tensor_name = 1; 538 | // pairs to annotate tensor specified by above. 539 | // The keys used in the mapping below must be pre-defined in ONNX spec. 540 | // For example, for 8-bit linear quantization case, 'SCALE_TENSOR', 'ZERO_POINT_TENSOR' will be pre-defined as 541 | // quantization parameter keys. 542 | repeated StringStringEntryProto quant_parameter_tensor_names = 2; 543 | } 544 | 545 | 546 | 547 | // Graphs 548 | // 549 | // A graph defines the computational logic of a model and is comprised of a parameterized 550 | // list of nodes that form a directed acyclic graph based on their inputs and outputs. 551 | // This is the equivalent of the "network" or "graph" in many deep learning 552 | // frameworks. 553 | message GraphProto { 554 | // The nodes in the graph, sorted topologically. 555 | repeated NodeProto node = 1; 556 | 557 | // The name of the graph. 558 | string name = 2; // namespace Graph 559 | 560 | // A list of named tensor values, used to specify constant inputs of the graph. 561 | // Each initializer (both TensorProto as well SparseTensorProto) MUST have a name. 562 | // The name MUST be unique across both initializer and sparse_initializer, 563 | // but the name MAY also appear in the input list. 564 | repeated TensorProto initializer = 5; 565 | 566 | // Initializers (see above) stored in sparse format. 567 | repeated SparseTensorProto sparse_initializer = 15; 568 | 569 | // A human-readable documentation for this graph. Markdown is allowed. 570 | string doc_string = 10; 571 | 572 | // The inputs and outputs of the graph. 573 | repeated ValueInfoProto input = 11; 574 | repeated ValueInfoProto output = 12; 575 | 576 | // Information for the values in the graph. The ValueInfoProto.name's 577 | // must be distinct. It is optional for a value to appear in value_info list. 578 | repeated ValueInfoProto value_info = 13; 579 | 580 | // This field carries information to indicate the mapping among a tensor and its 581 | // quantization parameter tensors. For example: 582 | // For tensor 'a', it may have {'SCALE_TENSOR', 'a_scale'} and {'ZERO_POINT_TENSOR', 'a_zero_point'} annotated, 583 | // which means, tensor 'a_scale' and tensor 'a_zero_point' are scale and zero point of tensor 'a' in the model. 584 | repeated TensorAnnotation quantization_annotation = 14; 585 | 586 | // Named metadata values; keys should be distinct. 587 | repeated StringStringEntryProto metadata_props = 16; 588 | 589 | reserved 3, 4, 6 to 9; 590 | reserved "ir_version", "producer_version", "producer_tag", "domain"; 591 | } 592 | 593 | // Tensors 594 | // 595 | // A serialized tensor value. 596 | message TensorProto { 597 | enum DataType { 598 | UNDEFINED = 0; 599 | // Basic types. 600 | FLOAT = 1; // float 601 | UINT8 = 2; // uint8_t 602 | INT8 = 3; // int8_t 603 | UINT16 = 4; // uint16_t 604 | INT16 = 5; // int16_t 605 | INT32 = 6; // int32_t 606 | INT64 = 7; // int64_t 607 | STRING = 8; // string 608 | BOOL = 9; // bool 609 | 610 | // IEEE754 half-precision floating-point format (16 bits wide). 611 | // This format has 1 sign bit, 5 exponent bits, and 10 mantissa bits. 612 | FLOAT16 = 10; 613 | 614 | DOUBLE = 11; 615 | UINT32 = 12; 616 | UINT64 = 13; 617 | COMPLEX64 = 14; // complex with float32 real and imaginary components 618 | COMPLEX128 = 15; // complex with float64 real and imaginary components 619 | 620 | // Non-IEEE floating-point format based on IEEE754 single-precision 621 | // floating-point number truncated to 16 bits. 622 | // This format has 1 sign bit, 8 exponent bits, and 7 mantissa bits. 623 | BFLOAT16 = 16; 624 | 625 | // Non-IEEE floating-point format based on papers 626 | // FP8 Formats for Deep Learning, https://arxiv.org/abs/2209.05433, 627 | // 8-bit Numerical Formats For Deep Neural Networks, https://arxiv.org/pdf/2206.02915.pdf. 628 | // Operators supported FP8 are Cast, CastLike, QuantizeLinear, DequantizeLinear. 629 | // The computation usually happens inside a block quantize / dequantize 630 | // fused by the runtime. 631 | FLOAT8E4M3FN = 17; // float 8, mostly used for coefficients, supports nan, not inf 632 | FLOAT8E4M3FNUZ = 18; // float 8, mostly used for coefficients, supports nan, not inf, no negative zero 633 | FLOAT8E5M2 = 19; // follows IEEE 754, supports nan, inf, mostly used for gradients 634 | FLOAT8E5M2FNUZ = 20; // follows IEEE 754, supports nan, not inf, mostly used for gradients, no negative zero 635 | 636 | // 4-bit integer data types 637 | UINT4 = 21; // Unsigned integer in range [0, 15] 638 | INT4 = 22; // Signed integer in range [-8, 7], using two's-complement representation 639 | 640 | // 4-bit floating point data types 641 | FLOAT4E2M1 = 23; 642 | 643 | // Future extensions go here. 644 | } 645 | 646 | // The shape of the tensor. 647 | repeated int64 dims = 1; 648 | 649 | // The data type of the tensor. 650 | // This field MUST have a valid TensorProto.DataType value 651 | int32 data_type = 2; 652 | 653 | // For very large tensors, we may want to store them in chunks, in which 654 | // case the following fields will specify the segment that is stored in 655 | // the current TensorProto. 656 | message Segment { 657 | int64 begin = 1; 658 | int64 end = 2; 659 | } 660 | Segment segment = 3; 661 | 662 | // Tensor content must be organized in row-major order. 663 | // 664 | // Depending on the data_type field, exactly one of the fields below with 665 | // name ending in _data is used to store the elements of the tensor. 666 | 667 | // For float and complex64 values 668 | // Complex64 tensors are encoded as a single array of floats, 669 | // with the real components appearing in odd numbered positions, 670 | // and the corresponding imaginary component appearing in the 671 | // subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i] 672 | // is encoded as [1.0, 2.0 ,3.0 ,4.0] 673 | // When this field is present, the data_type field MUST be FLOAT or COMPLEX64. 674 | repeated float float_data = 4 [packed = true]; 675 | 676 | // For int32, uint8, int8, uint16, int16, uint4, int4, bool, (b)float16, float8, and float4: 677 | // - (b)float16 and float8 values MUST be converted bit-wise into an unsigned integer 678 | // representation before being written to the buffer. 679 | // - Each pair of uint4, int4, and float4 values MUST be packed as two 4-bit elements into a single byte. 680 | // The first element is stored in the 4 least significant bits (LSB), 681 | // and the second element is stored in the 4 most significant bits (MSB). 682 | // 683 | // Consequently: 684 | // - For data types with a bit-width of 8 or greater, each `int32_data` stores one element. 685 | // - For 4-bit data types, each `int32_data` stores two elements. 686 | // 687 | // When this field is present, the data_type field MUST be 688 | // INT32, INT16, INT8, INT4, UINT16, UINT8, UINT4, BOOL, FLOAT16, BFLOAT16, FLOAT8E4M3FN, FLOAT8E4M3FNUZ, FLOAT8E5M2, FLOAT8E5M2FNUZ, FLOAT4E2M1 689 | repeated int32 int32_data = 5 [packed = true]; 690 | 691 | // For strings. 692 | // Each element of string_data is a UTF-8 encoded Unicode 693 | // string. No trailing null, no leading BOM. The protobuf "string" 694 | // scalar type is not used to match ML community conventions. 695 | // When this field is present, the data_type field MUST be STRING 696 | repeated bytes string_data = 6; 697 | 698 | // For int64. 699 | // When this field is present, the data_type field MUST be INT64 700 | repeated int64 int64_data = 7 [packed = true]; 701 | 702 | // Optionally, a name for the tensor. 703 | string name = 8; // namespace Value 704 | 705 | // A human-readable documentation for this tensor. Markdown is allowed. 706 | string doc_string = 12; 707 | 708 | // Serializations can either use one of the fields above, or use this 709 | // raw bytes field. The only exception is the string case, where one is 710 | // required to store the content in the repeated bytes string_data field. 711 | // 712 | // When this raw_data field is used to store tensor value, elements MUST 713 | // be stored in as fixed-width, little-endian order. 714 | // Floating-point data types MUST be stored in IEEE 754 format. 715 | // Complex64 elements must be written as two consecutive FLOAT values, real component first. 716 | // Complex128 elements must be written as two consecutive DOUBLE values, real component first. 717 | // Boolean type MUST be written one byte per tensor element (00000001 for true, 00000000 for false). 718 | // uint4 and int4 values must be packed to 4bitx2, the first element is stored in the 4 LSB and the second element is stored in the 4 MSB. 719 | // 720 | // Note: the advantage of specific field rather than the raw_data field is 721 | // that in some cases (e.g. int data), protobuf does a better packing via 722 | // variable length storage, and may lead to smaller binary footprint. 723 | // When this field is present, the data_type field MUST NOT be STRING or UNDEFINED 724 | bytes raw_data = 9; 725 | 726 | // Data can be stored inside the protobuf file using type-specific fields or raw_data. 727 | // Alternatively, raw bytes data can be stored in an external file, using the external_data field. 728 | // external_data stores key-value pairs describing data location. Recognized keys are: 729 | // - "location" (required) - POSIX filesystem path relative to the directory where the ONNX 730 | // protobuf model was stored 731 | // - "offset" (optional) - position of byte at which stored data begins. Integer stored as string. 732 | // Offset values SHOULD be multiples 4096 (page size) to enable mmap support. 733 | // - "length" (optional) - number of bytes containing data. Integer stored as string. 734 | // - "checksum" (optional) - SHA1 digest of file specified in under 'location' key. 735 | repeated StringStringEntryProto external_data = 13; 736 | 737 | // Location of the data for this tensor. MUST be one of: 738 | // - DEFAULT - data stored inside the protobuf message. Data is stored in raw_data (if set) otherwise in type-specified field. 739 | // - EXTERNAL - data stored in an external location as described by external_data field. 740 | enum DataLocation { 741 | DEFAULT = 0; 742 | EXTERNAL = 1; 743 | } 744 | 745 | // If value not set, data is stored in raw_data (if set) otherwise in type-specified field. 746 | DataLocation data_location = 14; 747 | 748 | // For double 749 | // Complex128 tensors are encoded as a single array of doubles, 750 | // with the real components appearing in odd numbered positions, 751 | // and the corresponding imaginary component appearing in the 752 | // subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i] 753 | // is encoded as [1.0, 2.0 ,3.0 ,4.0] 754 | // When this field is present, the data_type field MUST be DOUBLE or COMPLEX128 755 | repeated double double_data = 10 [packed = true]; 756 | 757 | // For uint64 and uint32 values 758 | // When this field is present, the data_type field MUST be 759 | // UINT32 or UINT64 760 | repeated uint64 uint64_data = 11 [packed = true]; 761 | 762 | // Named metadata values; keys should be distinct. 763 | repeated StringStringEntryProto metadata_props = 16; 764 | } 765 | 766 | // A serialized sparse-tensor value 767 | message SparseTensorProto { 768 | // The sequence of non-default values are encoded as a tensor of shape [NNZ]. 769 | // The default-value is zero for numeric tensors, and empty-string for string tensors. 770 | // values must have a non-empty name present which serves as a name for SparseTensorProto 771 | // when used in sparse_initializer list. 772 | TensorProto values = 1; 773 | 774 | // The indices of the non-default values, which may be stored in one of two formats. 775 | // (a) Indices can be a tensor of shape [NNZ, rank] with the [i,j]-th value 776 | // corresponding to the j-th index of the i-th value (in the values tensor). 777 | // (b) Indices can be a tensor of shape [NNZ], in which case the i-th value 778 | // must be the linearized-index of the i-th value (in the values tensor). 779 | // The linearized-index can be converted into an index tuple (k_1,...,k_rank) 780 | // using the shape provided below. 781 | // The indices must appear in ascending order without duplication. 782 | // In the first format, the ordering is lexicographic-ordering: 783 | // e.g., index-value [1,4] must appear before [2,1] 784 | TensorProto indices = 2; 785 | 786 | // The shape of the underlying dense-tensor: [dim_1, dim_2, ... dim_rank] 787 | repeated int64 dims = 3; 788 | } 789 | 790 | // Defines a tensor shape. A dimension can be either an integer value 791 | // or a symbolic variable. A symbolic variable represents an unknown 792 | // dimension. 793 | message TensorShapeProto { 794 | message Dimension { 795 | oneof value { 796 | int64 dim_value = 1; 797 | string dim_param = 2; // namespace Shape 798 | }; 799 | // Standard denotation can optionally be used to denote tensor 800 | // dimensions with standard semantic descriptions to ensure 801 | // that operations are applied to the correct axis of a tensor. 802 | // Refer to https://github.com/onnx/onnx/blob/main/docs/DimensionDenotation.md#denotation-definition 803 | // for pre-defined dimension denotations. 804 | string denotation = 3; 805 | }; 806 | repeated Dimension dim = 1; 807 | } 808 | 809 | // Types 810 | // 811 | // The standard ONNX data types. 812 | message TypeProto { 813 | 814 | message Tensor { 815 | // This field MUST NOT have the value of UNDEFINED 816 | // This field MUST have a valid TensorProto.DataType value 817 | // This field MUST be present for this version of the IR. 818 | int32 elem_type = 1; 819 | TensorShapeProto shape = 2; 820 | } 821 | 822 | // repeated T 823 | message Sequence { 824 | // The type and optional shape of each element of the sequence. 825 | // This field MUST be present for this version of the IR. 826 | TypeProto elem_type = 1; 827 | }; 828 | 829 | // map 830 | message Map { 831 | // This field MUST have a valid TensorProto.DataType value 832 | // This field MUST be present for this version of the IR. 833 | // This field MUST refer to an integral type ([U]INT{8|16|32|64}) or STRING 834 | int32 key_type = 1; 835 | // This field MUST be present for this version of the IR. 836 | TypeProto value_type = 2; 837 | }; 838 | 839 | // wrapper for Tensor, Sequence, or Map 840 | message Optional { 841 | // The type and optional shape of the element wrapped. 842 | // This field MUST be present for this version of the IR. 843 | // Possible values correspond to OptionalProto.DataType enum 844 | TypeProto elem_type = 1; 845 | }; 846 | 847 | 848 | message SparseTensor { 849 | // This field MUST NOT have the value of UNDEFINED 850 | // This field MUST have a valid TensorProto.DataType value 851 | // This field MUST be present for this version of the IR. 852 | int32 elem_type = 1; 853 | TensorShapeProto shape = 2; 854 | } 855 | 856 | 857 | oneof value { 858 | // The type of a tensor. 859 | Tensor tensor_type = 1; 860 | 861 | // NOTE: DNN-only implementations of ONNX MAY elect to not support non-tensor values 862 | // as input and output to graphs and nodes. These types are needed to naturally 863 | // support classical ML operators. DNN operators SHOULD restrict their input 864 | // and output types to tensors. 865 | 866 | // The type of a sequence. 867 | Sequence sequence_type = 4; 868 | 869 | // The type of a map. 870 | Map map_type = 5; 871 | 872 | // The type of an optional. 873 | Optional optional_type = 9; 874 | 875 | 876 | // Type of the sparse tensor 877 | SparseTensor sparse_tensor_type = 8; 878 | 879 | } 880 | 881 | // An optional denotation can be used to denote the whole 882 | // type with a standard semantic description as to what is 883 | // stored inside. Refer to https://github.com/onnx/onnx/blob/main/docs/TypeDenotation.md#type-denotation-definition 884 | // for pre-defined type denotations. 885 | string denotation = 6; 886 | } 887 | 888 | // Operator Sets 889 | // 890 | // OperatorSets are uniquely identified by a (domain, opset_version) pair. 891 | message OperatorSetIdProto { 892 | // The domain of the operator set being identified. 893 | // The empty string ("") or absence of this field implies the operator 894 | // set that is defined as part of the ONNX specification. 895 | // This field MUST be present in this version of the IR when referring to any other operator set. 896 | string domain = 1; 897 | 898 | // The version of the operator set being identified. 899 | // This field MUST be present in this version of the IR. 900 | int64 version = 2; 901 | } 902 | 903 | // Operator/function status. 904 | enum OperatorStatus { 905 | EXPERIMENTAL = 0; 906 | STABLE = 1; 907 | } 908 | 909 | message FunctionProto { 910 | // The name of the function, similar to op_type in NodeProto. 911 | // This is part of the unique-id (domain, name, overload) of FunctionProtos in a model. 912 | string name = 1; 913 | 914 | // Deprecated since IR Version 8 915 | // optional int64 since_version = 2; 916 | reserved 2; 917 | reserved "since_version"; 918 | 919 | // Deprecated since IR Version 8 920 | // optional OperatorStatus status = 3; 921 | reserved 3; 922 | reserved "status"; 923 | 924 | // The inputs and outputs of the function. 925 | repeated string input = 4; 926 | repeated string output = 5; 927 | 928 | // The attribute parameters of the function. 929 | // It is for function parameters without default values. 930 | repeated string attribute = 6; 931 | 932 | // The attribute protos of the function. 933 | // It is for function attributes with default values. 934 | // A function attribute shall be represented either as 935 | // a string attribute or an AttributeProto, not both. 936 | repeated AttributeProto attribute_proto = 11; 937 | 938 | // The nodes in the function. 939 | repeated NodeProto node = 7; 940 | // A human-readable documentation for this function. Markdown is allowed. 941 | string doc_string = 8; 942 | 943 | // The OperatorSets this function body (graph) relies on. 944 | // 945 | // All nodes in the function body (graph) will bind against the operator 946 | // with the same-domain/same-op_type operator with the HIGHEST version 947 | // in the referenced operator sets. This means at most one version can be relied 948 | // for one domain. 949 | // 950 | // The operator sets imported by FunctionProto should be compatible with the ones 951 | // imported by ModelProto. Example, if same operator set say 'A' is imported by FunctionProto 952 | // and ModelProto then versions for the operator set may be different but, 953 | // the operator schema returned for op_type, domain, version combination 954 | // for both the versions should be same. 955 | 956 | repeated OperatorSetIdProto opset_import = 9; 957 | 958 | // The domain which this function belongs to. 959 | // This is part of the unique-id (domain, name, overload) of FunctionProtos in a model. 960 | string domain = 10; 961 | 962 | // The overload identifier of the function. 963 | // This is part of the unique-id (domain, name, overload) of FunctionProtos in a model. 964 | string overload = 13; 965 | 966 | // Information for the values in the function. The ValueInfoProto.name's 967 | // must be distinct and refer to names in the function (including inputs, 968 | // outputs, and intermediate values). It is optional for a value to appear 969 | // in value_info list. 970 | repeated ValueInfoProto value_info = 12; 971 | 972 | // Named metadata values; keys should be distinct. 973 | repeated StringStringEntryProto metadata_props = 14; 974 | } 975 | 976 | // For using protobuf-lite 977 | option optimize_for = LITE_RUNTIME; 978 | -------------------------------------------------------------------------------- /onnx/protobuf-c.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2025, Dave Benson and the protobuf-c authors. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following disclaimer 14 | * in the documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /*! \file 31 | * \mainpage Introduction 32 | * 33 | * This is [protobuf-c], a C implementation of [Protocol Buffers]. 34 | * 35 | * This file defines the public API for the `libprotobuf-c` support library. 36 | * This API includes interfaces that can be used directly by client code as well 37 | * as the interfaces used by the code generated by the `protoc-gen-c` compiler 38 | * plugin. 39 | * 40 | * The `libprotobuf-c` support library performs the actual serialization and 41 | * deserialization of Protocol Buffers messages. It interacts with structures, 42 | * definitions, and metadata generated by the `protoc-gen-c` compiler plugin 43 | * from .proto files. 44 | * 45 | * \authors Dave Benson and the `protobuf-c` authors. 46 | * 47 | * \copyright 2008-2025. Licensed under the terms of the [BSD-2-Clause] license. 48 | * 49 | * [protobuf-c]: https://github.com/protobuf-c/protobuf-c 50 | * [Protocol Buffers]: https://developers.google.com/protocol-buffers/ 51 | * [BSD-2-Clause]: http://opensource.org/licenses/BSD-2-Clause 52 | * 53 | * \page gencode Generated Code 54 | * 55 | * For each enum, we generate a C enum. For each message, we generate a C 56 | * structure which can be cast to a `ProtobufCMessage`. 57 | * 58 | * For each enum and message, we generate a descriptor object that allows us to 59 | * implement a kind of reflection on the structures. 60 | * 61 | * First, some naming conventions: 62 | * 63 | * - The name of the type for enums and messages and services is camel case 64 | * (meaning WordsAreCrammedTogether) except that double underscores are used 65 | * to delimit scopes. For example, the following `.proto` file: 66 | * 67 | ~~~{.proto} 68 | package foo.bar; 69 | message BazBah { 70 | optional int32 val = 1; 71 | } 72 | ~~~ 73 | * 74 | * would generate a C type `Foo__Bar__BazBah`. 75 | * 76 | * - Identifiers for functions and globals are all lowercase, with camel case 77 | * words separated by single underscores. For example, one of the function 78 | * prototypes generated by `protoc-gen-c` for the above example: 79 | * 80 | ~~~{.c} 81 | Foo__Bar__BazBah * 82 | foo__bar__baz_bah__unpack 83 | (ProtobufCAllocator *allocator, 84 | size_t len, 85 | const uint8_t *data); 86 | ~~~ 87 | * 88 | * - Identifiers for enum values contain an uppercase prefix which embeds the 89 | * package name and the enum type name. 90 | * 91 | * - A double underscore is used to separate further components of identifier 92 | * names. 93 | * 94 | * For example, in the name of the unpack function above, the package name 95 | * `foo.bar` has become `foo__bar`, the message name BazBah has become 96 | * `baz_bah`, and the method name is `unpack`. These are all joined with double 97 | * underscores to form the C identifier `foo__bar__baz_bah__unpack`. 98 | * 99 | * We also generate descriptor objects for messages and enums. These are 100 | * declared in the `.pb-c.h` files: 101 | * 102 | ~~~{.c} 103 | extern const ProtobufCMessageDescriptor foo__bar__baz_bah__descriptor; 104 | ~~~ 105 | * 106 | * The message structures all begin with `ProtobufCMessageDescriptor *` which is 107 | * sufficient to allow them to be cast to `ProtobufCMessage`. 108 | * 109 | * For each message defined in a `.proto` file, we generate a number of 110 | * functions and macros. Each function name contains a prefix based on the 111 | * package name and message name in order to make it a unique C identifier. 112 | * 113 | * - `INIT`. Statically initializes a message object, initializing its 114 | * descriptor and setting its fields to default values. Uninitialized 115 | * messages cannot be processed by the protobuf-c library. 116 | * 117 | ~~~{.c} 118 | #define FOO__BAR__BAZ_BAH__INIT \ 119 | { PROTOBUF_C_MESSAGE_INIT (&foo__bar__baz_bah__descriptor), 0 } 120 | ~~~ 121 | * - `init()`. Initializes a message object, initializing its descriptor and 122 | * setting its fields to default values. Uninitialized messages cannot be 123 | * processed by the protobuf-c library. 124 | * 125 | ~~~{.c} 126 | void foo__bar__baz_bah__init 127 | (Foo__Bar__BazBah *message); 128 | ~~~ 129 | * - `unpack()`. Unpacks data for a particular message format. Note that the 130 | * `allocator` parameter is usually `NULL` to indicate that the system's 131 | * `malloc()` and `free()` functions should be used for dynamically allocating 132 | * memory. 133 | * 134 | ~~~{.c} 135 | Foo__Bar__BazBah * 136 | foo__bar__baz_bah__unpack 137 | (ProtobufCAllocator *allocator, 138 | size_t len, 139 | const uint8_t *data); 140 | ~~~ 141 | * 142 | * - `free_unpacked()`. Frees a message object obtained with the `unpack()` 143 | * method. Freeing `NULL` is allowed (the same as with `free()`). 144 | * 145 | ~~~{.c} 146 | void foo__bar__baz_bah__free_unpacked 147 | (Foo__Bar__BazBah *message, 148 | ProtobufCAllocator *allocator); 149 | ~~~ 150 | * 151 | * - `get_packed_size()`. Calculates the length in bytes of the serialized 152 | * representation of the message object. 153 | * 154 | ~~~{.c} 155 | size_t foo__bar__baz_bah__get_packed_size 156 | (const Foo__Bar__BazBah *message); 157 | ~~~ 158 | * 159 | * - `pack()`. Pack a message object into a preallocated buffer. Assumes that 160 | * the buffer is large enough. (Use `get_packed_size()` first.) 161 | * 162 | ~~~{.c} 163 | size_t foo__bar__baz_bah__pack 164 | (const Foo__Bar__BazBah *message, 165 | uint8_t *out); 166 | ~~~ 167 | * 168 | * - `pack_to_buffer()`. Packs a message into a "virtual buffer". This is an 169 | * object which defines an "append bytes" callback to consume data as it is 170 | * serialized. 171 | * 172 | ~~~{.c} 173 | size_t foo__bar__baz_bah__pack_to_buffer 174 | (const Foo__Bar__BazBah *message, 175 | ProtobufCBuffer *buffer); 176 | ~~~ 177 | * 178 | * \page pack Packing and unpacking messages 179 | * 180 | * To pack a message, first compute the packed size of the message with 181 | * protobuf_c_message_get_packed_size(), then allocate a buffer of at least 182 | * that size, then call protobuf_c_message_pack(). 183 | * 184 | * Alternatively, a message can be serialized without calculating the final size 185 | * first. Use the protobuf_c_message_pack_to_buffer() function and provide a 186 | * ProtobufCBuffer object which implements an "append" method that consumes 187 | * data. 188 | * 189 | * To unpack a message, call the protobuf_c_message_unpack() function. The 190 | * result can be cast to an object of the type that matches the descriptor for 191 | * the message. 192 | * 193 | * The result of unpacking a message should be freed with 194 | * protobuf_c_message_free_unpacked(). 195 | */ 196 | 197 | #ifndef PROTOBUF_C_H 198 | #define PROTOBUF_C_H 199 | 200 | #include 201 | #include 202 | #include 203 | #include 204 | 205 | #ifdef __cplusplus 206 | # define PROTOBUF_C__BEGIN_DECLS extern "C" { 207 | # define PROTOBUF_C__END_DECLS } 208 | #else 209 | # define PROTOBUF_C__BEGIN_DECLS 210 | # define PROTOBUF_C__END_DECLS 211 | #endif 212 | 213 | PROTOBUF_C__BEGIN_DECLS 214 | 215 | #if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB) 216 | # ifdef PROTOBUF_C_EXPORT 217 | # define PROTOBUF_C__API __declspec(dllexport) 218 | # else 219 | # define PROTOBUF_C__API __declspec(dllimport) 220 | # endif 221 | #else 222 | # define PROTOBUF_C__API 223 | #endif 224 | 225 | #if !defined(PROTOBUF_C__NO_DEPRECATED) && \ 226 | ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) 227 | # define PROTOBUF_C__DEPRECATED __attribute__((__deprecated__)) 228 | #else 229 | # define PROTOBUF_C__DEPRECATED 230 | #endif 231 | 232 | #ifndef PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE 233 | #define PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(enum_name) \ 234 | , _##enum_name##_IS_INT_SIZE = INT_MAX 235 | #endif 236 | 237 | #define PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC 0x14159bc3 238 | #define PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC 0x28aaeef9 239 | #define PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC 0x114315af 240 | 241 | /* Empty string used for initializers */ 242 | #if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB) 243 | static const char protobuf_c_empty_string[] = ""; 244 | #else 245 | extern const char protobuf_c_empty_string[]; 246 | #endif 247 | 248 | /** 249 | * \defgroup api Public API 250 | * 251 | * This is the public API for `libprotobuf-c`. These interfaces are stable and 252 | * subject to Semantic Versioning guarantees. 253 | * 254 | * @{ 255 | */ 256 | 257 | /** 258 | * Values for the `flags` word in `ProtobufCFieldDescriptor`. 259 | */ 260 | typedef enum { 261 | /** Set if the field is repeated and marked with the `packed` option. */ 262 | PROTOBUF_C_FIELD_FLAG_PACKED = (1 << 0), 263 | 264 | /** Set if the field is marked with the `deprecated` option. */ 265 | PROTOBUF_C_FIELD_FLAG_DEPRECATED = (1 << 1), 266 | 267 | /** Set if the field is a member of a oneof (union). */ 268 | PROTOBUF_C_FIELD_FLAG_ONEOF = (1 << 2), 269 | } ProtobufCFieldFlag; 270 | 271 | /** 272 | * Message field rules. 273 | * 274 | * \see [Defining A Message Type] in the Protocol Buffers documentation. 275 | * 276 | * [Defining A Message Type]: 277 | * https://developers.google.com/protocol-buffers/docs/proto#simple 278 | */ 279 | typedef enum { 280 | /** A well-formed message must have exactly one of this field. */ 281 | PROTOBUF_C_LABEL_REQUIRED, 282 | 283 | /** 284 | * A well-formed message can have zero or one of this field (but not 285 | * more than one). 286 | */ 287 | PROTOBUF_C_LABEL_OPTIONAL, 288 | 289 | /** 290 | * This field can be repeated any number of times (including zero) in a 291 | * well-formed message. The order of the repeated values will be 292 | * preserved. 293 | */ 294 | PROTOBUF_C_LABEL_REPEATED, 295 | 296 | /** 297 | * This field has no label. This is valid only in proto3 and is 298 | * equivalent to OPTIONAL but no "has" quantifier will be consulted. 299 | */ 300 | PROTOBUF_C_LABEL_NONE, 301 | } ProtobufCLabel; 302 | 303 | /** 304 | * Field value types. 305 | * 306 | * \see [Scalar Value Types] in the Protocol Buffers documentation. 307 | * 308 | * [Scalar Value Types]: 309 | * https://developers.google.com/protocol-buffers/docs/proto#scalar 310 | */ 311 | typedef enum { 312 | PROTOBUF_C_TYPE_INT32, /**< int32 */ 313 | PROTOBUF_C_TYPE_SINT32, /**< signed int32 */ 314 | PROTOBUF_C_TYPE_SFIXED32, /**< signed int32 (4 bytes) */ 315 | PROTOBUF_C_TYPE_INT64, /**< int64 */ 316 | PROTOBUF_C_TYPE_SINT64, /**< signed int64 */ 317 | PROTOBUF_C_TYPE_SFIXED64, /**< signed int64 (8 bytes) */ 318 | PROTOBUF_C_TYPE_UINT32, /**< unsigned int32 */ 319 | PROTOBUF_C_TYPE_FIXED32, /**< unsigned int32 (4 bytes) */ 320 | PROTOBUF_C_TYPE_UINT64, /**< unsigned int64 */ 321 | PROTOBUF_C_TYPE_FIXED64, /**< unsigned int64 (8 bytes) */ 322 | PROTOBUF_C_TYPE_FLOAT, /**< float */ 323 | PROTOBUF_C_TYPE_DOUBLE, /**< double */ 324 | PROTOBUF_C_TYPE_BOOL, /**< boolean */ 325 | PROTOBUF_C_TYPE_ENUM, /**< enumerated type */ 326 | PROTOBUF_C_TYPE_STRING, /**< UTF-8 or ASCII string */ 327 | PROTOBUF_C_TYPE_BYTES, /**< arbitrary byte sequence */ 328 | PROTOBUF_C_TYPE_MESSAGE, /**< nested message */ 329 | } ProtobufCType; 330 | 331 | /** 332 | * Field wire types. 333 | * 334 | * \see [Message Structure] in the Protocol Buffers documentation. 335 | * 336 | * [Message Structure]: 337 | * https://developers.google.com/protocol-buffers/docs/encoding#structure 338 | */ 339 | typedef enum { 340 | PROTOBUF_C_WIRE_TYPE_VARINT = 0, 341 | PROTOBUF_C_WIRE_TYPE_64BIT = 1, 342 | PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED = 2, 343 | /* "Start group" and "end group" wire types are unsupported. */ 344 | PROTOBUF_C_WIRE_TYPE_32BIT = 5, 345 | } ProtobufCWireType; 346 | 347 | struct ProtobufCAllocator; 348 | struct ProtobufCBinaryData; 349 | struct ProtobufCBuffer; 350 | struct ProtobufCBufferSimple; 351 | struct ProtobufCEnumDescriptor; 352 | struct ProtobufCEnumValue; 353 | struct ProtobufCEnumValueIndex; 354 | struct ProtobufCFieldDescriptor; 355 | struct ProtobufCIntRange; 356 | struct ProtobufCMessage; 357 | struct ProtobufCMessageDescriptor; 358 | struct ProtobufCMessageUnknownField; 359 | struct ProtobufCMethodDescriptor; 360 | struct ProtobufCService; 361 | struct ProtobufCServiceDescriptor; 362 | 363 | typedef struct ProtobufCAllocator ProtobufCAllocator; 364 | typedef struct ProtobufCBinaryData ProtobufCBinaryData; 365 | typedef struct ProtobufCBuffer ProtobufCBuffer; 366 | typedef struct ProtobufCBufferSimple ProtobufCBufferSimple; 367 | typedef struct ProtobufCEnumDescriptor ProtobufCEnumDescriptor; 368 | typedef struct ProtobufCEnumValue ProtobufCEnumValue; 369 | typedef struct ProtobufCEnumValueIndex ProtobufCEnumValueIndex; 370 | typedef struct ProtobufCFieldDescriptor ProtobufCFieldDescriptor; 371 | typedef struct ProtobufCIntRange ProtobufCIntRange; 372 | typedef struct ProtobufCMessage ProtobufCMessage; 373 | typedef struct ProtobufCMessageDescriptor ProtobufCMessageDescriptor; 374 | typedef struct ProtobufCMessageUnknownField ProtobufCMessageUnknownField; 375 | typedef struct ProtobufCMethodDescriptor ProtobufCMethodDescriptor; 376 | typedef struct ProtobufCService ProtobufCService; 377 | typedef struct ProtobufCServiceDescriptor ProtobufCServiceDescriptor; 378 | 379 | /** Boolean type. */ 380 | typedef int protobuf_c_boolean; 381 | 382 | typedef void (*ProtobufCClosure)(const ProtobufCMessage *, void *closure_data); 383 | typedef void (*ProtobufCMessageInit)(ProtobufCMessage *); 384 | typedef void (*ProtobufCServiceDestroy)(ProtobufCService *); 385 | 386 | /** 387 | * Structure for defining a custom memory allocator. 388 | */ 389 | struct ProtobufCAllocator { 390 | /** Function to allocate memory. */ 391 | void *(*alloc)(void *allocator_data, size_t size); 392 | 393 | /** Function to free memory. */ 394 | void (*free)(void *allocator_data, void *pointer); 395 | 396 | /** Opaque pointer passed to `alloc` and `free` functions. */ 397 | void *allocator_data; 398 | }; 399 | 400 | /** 401 | * Structure for the protobuf `bytes` scalar type. 402 | * 403 | * The data contained in a `ProtobufCBinaryData` is an arbitrary sequence of 404 | * bytes. It may contain embedded `NUL` characters and is not required to be 405 | * `NUL`-terminated. 406 | */ 407 | struct ProtobufCBinaryData { 408 | size_t len; /**< Number of bytes in the `data` field. */ 409 | uint8_t *data; /**< Data bytes. */ 410 | }; 411 | 412 | /** 413 | * Structure for defining a virtual append-only buffer. Used by 414 | * protobuf_c_message_pack_to_buffer() to abstract the consumption of serialized 415 | * bytes. 416 | * 417 | * `ProtobufCBuffer` "subclasses" may be defined on the stack. For example, to 418 | * write to a `FILE` object: 419 | * 420 | ~~~{.c} 421 | typedef struct { 422 | ProtobufCBuffer base; 423 | FILE *fp; 424 | } BufferAppendToFile; 425 | 426 | static void 427 | my_buffer_file_append(ProtobufCBuffer *buffer, 428 | size_t len, 429 | const uint8_t *data) 430 | { 431 | BufferAppendToFile *file_buf = (BufferAppendToFile *) buffer; 432 | fwrite(data, len, 1, file_buf->fp); // XXX: No error handling! 433 | } 434 | ~~~ 435 | * 436 | * To use this new type of ProtobufCBuffer, it could be called as follows: 437 | * 438 | ~~~{.c} 439 | ... 440 | BufferAppendToFile tmp = {0}; 441 | tmp.base.append = my_buffer_file_append; 442 | tmp.fp = fp; 443 | protobuf_c_message_pack_to_buffer(&message, &tmp); 444 | ... 445 | ~~~ 446 | */ 447 | struct ProtobufCBuffer { 448 | /** Append function. Consumes the `len` bytes stored at `data`. */ 449 | void (*append)(ProtobufCBuffer *buffer, 450 | size_t len, 451 | const uint8_t *data); 452 | }; 453 | 454 | /** 455 | * Simple buffer "subclass" of `ProtobufCBuffer`. 456 | * 457 | * A `ProtobufCBufferSimple` object is declared on the stack and uses a 458 | * scratch buffer provided by the user for the initial allocation. It performs 459 | * exponential resizing, using dynamically allocated memory. A 460 | * `ProtobufCBufferSimple` object can be created and used as follows: 461 | * 462 | ~~~{.c} 463 | uint8_t pad[128]; 464 | ProtobufCBufferSimple simple = PROTOBUF_C_BUFFER_SIMPLE_INIT(pad); 465 | ProtobufCBuffer *buffer = (ProtobufCBuffer *) &simple; 466 | ~~~ 467 | * 468 | * `buffer` can now be used with `protobuf_c_message_pack_to_buffer()`. Once a 469 | * message has been serialized to a `ProtobufCBufferSimple` object, the 470 | * serialized data bytes can be accessed from the `.data` field. 471 | * 472 | * To free the memory allocated by a `ProtobufCBufferSimple` object, if any, 473 | * call PROTOBUF_C_BUFFER_SIMPLE_CLEAR() on the object, for example: 474 | * 475 | ~~~{.c} 476 | PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&simple); 477 | ~~~ 478 | * 479 | * \see PROTOBUF_C_BUFFER_SIMPLE_INIT 480 | * \see PROTOBUF_C_BUFFER_SIMPLE_CLEAR 481 | */ 482 | struct ProtobufCBufferSimple { 483 | /** "Base class". */ 484 | ProtobufCBuffer base; 485 | /** Number of bytes allocated in `data`. */ 486 | size_t alloced; 487 | /** Number of bytes currently stored in `data`. */ 488 | size_t len; 489 | /** Data bytes. */ 490 | uint8_t *data; 491 | /** Whether `data` must be freed. */ 492 | protobuf_c_boolean must_free_data; 493 | /** Allocator to use. May be NULL to indicate the system allocator. */ 494 | ProtobufCAllocator *allocator; 495 | }; 496 | 497 | /** 498 | * Describes an enumeration as a whole, with all of its values. 499 | */ 500 | struct ProtobufCEnumDescriptor { 501 | /** Magic value checked to ensure that the API is used correctly. */ 502 | uint32_t magic; 503 | 504 | /** The qualified name (e.g., "namespace.Type"). */ 505 | const char *name; 506 | /** The unqualified name as given in the .proto file (e.g., "Type"). */ 507 | const char *short_name; 508 | /** Identifier used in generated C code. */ 509 | const char *c_name; 510 | /** The dot-separated namespace. */ 511 | const char *package_name; 512 | 513 | /** Number elements in `values`. */ 514 | unsigned n_values; 515 | /** Array of distinct values, sorted by numeric value. */ 516 | const ProtobufCEnumValue *values; 517 | 518 | /** Number of elements in `values_by_name`. */ 519 | unsigned n_value_names; 520 | /** Array of named values, including aliases, sorted by name. */ 521 | const ProtobufCEnumValueIndex *values_by_name; 522 | 523 | /** Number of elements in `value_ranges`. */ 524 | unsigned n_value_ranges; 525 | /** Value ranges, for faster lookups by numeric value. */ 526 | const ProtobufCIntRange *value_ranges; 527 | 528 | /** Reserved for future use. */ 529 | void *reserved1; 530 | /** Reserved for future use. */ 531 | void *reserved2; 532 | /** Reserved for future use. */ 533 | void *reserved3; 534 | /** Reserved for future use. */ 535 | void *reserved4; 536 | }; 537 | 538 | /** 539 | * Represents a single value of an enumeration. 540 | */ 541 | struct ProtobufCEnumValue { 542 | /** The string identifying this value in the .proto file. */ 543 | const char *name; 544 | 545 | /** The string identifying this value in generated C code. */ 546 | const char *c_name; 547 | 548 | /** The numeric value assigned in the .proto file. */ 549 | int value; 550 | }; 551 | 552 | /** 553 | * Used by `ProtobufCEnumDescriptor` to look up enum values. 554 | */ 555 | struct ProtobufCEnumValueIndex { 556 | /** Name of the enum value. */ 557 | const char *name; 558 | /** Index into values[] array. */ 559 | unsigned index; 560 | }; 561 | 562 | /** 563 | * Describes a single field in a message. 564 | */ 565 | struct ProtobufCFieldDescriptor { 566 | /** Name of the field as given in the .proto file. */ 567 | const char *name; 568 | 569 | /** Tag value of the field as given in the .proto file. */ 570 | uint32_t id; 571 | 572 | /** Whether the field is `REQUIRED`, `OPTIONAL`, or `REPEATED`. */ 573 | ProtobufCLabel label; 574 | 575 | /** The type of the field. */ 576 | ProtobufCType type; 577 | 578 | /** 579 | * The offset in bytes of the message's C structure's quantifier field 580 | * (the `has_MEMBER` field for optional members or the `n_MEMBER` field 581 | * for repeated members or the case enum for oneofs). 582 | */ 583 | unsigned quantifier_offset; 584 | 585 | /** 586 | * The offset in bytes into the message's C structure for the member 587 | * itself. 588 | */ 589 | unsigned offset; 590 | 591 | /** 592 | * A type-specific descriptor. 593 | * 594 | * If `type` is `PROTOBUF_C_TYPE_ENUM`, then `descriptor` points to the 595 | * corresponding `ProtobufCEnumDescriptor`. 596 | * 597 | * If `type` is `PROTOBUF_C_TYPE_MESSAGE`, then `descriptor` points to 598 | * the corresponding `ProtobufCMessageDescriptor`. 599 | * 600 | * Otherwise this field is NULL. 601 | */ 602 | const void *descriptor; /* for MESSAGE and ENUM types */ 603 | 604 | /** The default value for this field, if defined. May be NULL. */ 605 | const void *default_value; 606 | 607 | /** 608 | * A flag word. Zero or more of the bits defined in the 609 | * `ProtobufCFieldFlag` enum may be set. 610 | */ 611 | uint32_t flags; 612 | 613 | /** Reserved for future use. */ 614 | unsigned reserved_flags; 615 | /** Reserved for future use. */ 616 | void *reserved2; 617 | /** Reserved for future use. */ 618 | void *reserved3; 619 | }; 620 | 621 | /** 622 | * Helper structure for optimizing int => index lookups in the case 623 | * where the keys are mostly consecutive values, as they presumably are for 624 | * enums and fields. 625 | * 626 | * The data structures requires that the values in the original array are 627 | * sorted. 628 | */ 629 | struct ProtobufCIntRange { 630 | int start_value; 631 | unsigned orig_index; 632 | /* 633 | * NOTE: the number of values in the range can be inferred by looking 634 | * at the next element's orig_index. A dummy element is added to make 635 | * this simple. 636 | */ 637 | }; 638 | 639 | /** 640 | * An instance of a message. 641 | * 642 | * `ProtobufCMessage` is a light-weight "base class" for all messages. 643 | * 644 | * In particular, `ProtobufCMessage` doesn't have any allocation policy 645 | * associated with it. That's because it's common to create `ProtobufCMessage` 646 | * objects on the stack. In fact, that's what we recommend for sending messages. 647 | * If the object is allocated from the stack, you can't really have a memory 648 | * leak. 649 | * 650 | * This means that calls to functions like protobuf_c_message_unpack() which 651 | * return a `ProtobufCMessage` must be paired with a call to a free function, 652 | * like protobuf_c_message_free_unpacked(). 653 | */ 654 | struct ProtobufCMessage { 655 | /** The descriptor for this message type. */ 656 | const ProtobufCMessageDescriptor *descriptor; 657 | /** The number of elements in `unknown_fields`. */ 658 | unsigned n_unknown_fields; 659 | /** The fields that weren't recognized by the parser. */ 660 | ProtobufCMessageUnknownField *unknown_fields; 661 | }; 662 | 663 | /** 664 | * Describes a message. 665 | */ 666 | struct ProtobufCMessageDescriptor { 667 | /** Magic value checked to ensure that the API is used correctly. */ 668 | uint32_t magic; 669 | 670 | /** The qualified name (e.g., "namespace.Type"). */ 671 | const char *name; 672 | /** The unqualified name as given in the .proto file (e.g., "Type"). */ 673 | const char *short_name; 674 | /** Identifier used in generated C code. */ 675 | const char *c_name; 676 | /** The dot-separated namespace. */ 677 | const char *package_name; 678 | 679 | /** 680 | * Size in bytes of the C structure representing an instance of this 681 | * type of message. 682 | */ 683 | size_t sizeof_message; 684 | 685 | /** Number of elements in `fields`. */ 686 | unsigned n_fields; 687 | /** Field descriptors, sorted by tag number. */ 688 | const ProtobufCFieldDescriptor *fields; 689 | /** Used for looking up fields by name. */ 690 | const unsigned *fields_sorted_by_name; 691 | 692 | /** Number of elements in `field_ranges`. */ 693 | unsigned n_field_ranges; 694 | /** Used for looking up fields by id. */ 695 | const ProtobufCIntRange *field_ranges; 696 | 697 | /** Message initialisation function. */ 698 | ProtobufCMessageInit message_init; 699 | 700 | /** Reserved for future use. */ 701 | void *reserved1; 702 | /** Reserved for future use. */ 703 | void *reserved2; 704 | /** Reserved for future use. */ 705 | void *reserved3; 706 | }; 707 | 708 | /** 709 | * An unknown message field. 710 | */ 711 | struct ProtobufCMessageUnknownField { 712 | /** The tag number. */ 713 | uint32_t tag; 714 | /** The wire type of the field. */ 715 | ProtobufCWireType wire_type; 716 | /** Number of bytes in `data`. */ 717 | size_t len; 718 | /** Field data. */ 719 | uint8_t *data; 720 | }; 721 | 722 | /** 723 | * Method descriptor. 724 | */ 725 | struct ProtobufCMethodDescriptor { 726 | /** Method name. */ 727 | const char *name; 728 | /** Input message descriptor. */ 729 | const ProtobufCMessageDescriptor *input; 730 | /** Output message descriptor. */ 731 | const ProtobufCMessageDescriptor *output; 732 | }; 733 | 734 | /** 735 | * Service. 736 | */ 737 | struct ProtobufCService { 738 | /** Service descriptor. */ 739 | const ProtobufCServiceDescriptor *descriptor; 740 | /** Function to invoke the service. */ 741 | void (*invoke)(ProtobufCService *service, 742 | unsigned method_index, 743 | const ProtobufCMessage *input, 744 | ProtobufCClosure closure, 745 | void *closure_data); 746 | /** Function to destroy the service. */ 747 | void (*destroy)(ProtobufCService *service); 748 | }; 749 | 750 | /** 751 | * Service descriptor. 752 | */ 753 | struct ProtobufCServiceDescriptor { 754 | /** Magic value checked to ensure that the API is used correctly. */ 755 | uint32_t magic; 756 | 757 | /** Service name. */ 758 | const char *name; 759 | /** Short version of service name. */ 760 | const char *short_name; 761 | /** C identifier for the service name. */ 762 | const char *c_name; 763 | /** Package name. */ 764 | const char *package; 765 | /** Number of elements in `methods`. */ 766 | unsigned n_methods; 767 | /** Method descriptors, in the order defined in the .proto file. */ 768 | const ProtobufCMethodDescriptor *methods; 769 | /** Sort index of methods. */ 770 | const unsigned *method_indices_by_name; 771 | }; 772 | 773 | /** 774 | * Get the version of the protobuf-c library. Note that this is the version of 775 | * the library linked against, not the version of the headers compiled against. 776 | * 777 | * \return A string containing the version number of protobuf-c. 778 | */ 779 | PROTOBUF_C__API 780 | const char * 781 | protobuf_c_version(void); 782 | 783 | /** 784 | * Get the version of the protobuf-c library. Note that this is the version of 785 | * the library linked against, not the version of the headers compiled against. 786 | * 787 | * \return A 32 bit unsigned integer containing the version number of 788 | * protobuf-c, represented in base-10 as (MAJOR*1E6) + (MINOR*1E3) + PATCH. 789 | */ 790 | PROTOBUF_C__API 791 | uint32_t 792 | protobuf_c_version_number(void); 793 | 794 | /** 795 | * The version of the protobuf-c headers, represented as a string using the same 796 | * format as protobuf_c_version(). 797 | */ 798 | #define PROTOBUF_C_VERSION "1.5.2" 799 | 800 | /** 801 | * The version of the protobuf-c headers, represented as an integer using the 802 | * same format as protobuf_c_version_number(). 803 | */ 804 | #define PROTOBUF_C_VERSION_NUMBER 1005002 805 | 806 | /** 807 | * The minimum protoc-gen-c version which works with the current version of the 808 | * protobuf-c headers. 809 | */ 810 | #define PROTOBUF_C_MIN_COMPILER_VERSION 1000000 811 | 812 | /** 813 | * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by name. 814 | * 815 | * \param desc 816 | * The `ProtobufCEnumDescriptor` object. 817 | * \param name 818 | * The `name` field from the corresponding `ProtobufCEnumValue` object to 819 | * match. 820 | * \return 821 | * A `ProtobufCEnumValue` object. 822 | * \retval NULL 823 | * If not found or if the optimize_for = CODE_SIZE option was set. 824 | */ 825 | PROTOBUF_C__API 826 | const ProtobufCEnumValue * 827 | protobuf_c_enum_descriptor_get_value_by_name( 828 | const ProtobufCEnumDescriptor *desc, 829 | const char *name); 830 | 831 | /** 832 | * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by numeric 833 | * value. 834 | * 835 | * \param desc 836 | * The `ProtobufCEnumDescriptor` object. 837 | * \param value 838 | * The `value` field from the corresponding `ProtobufCEnumValue` object to 839 | * match. 840 | * 841 | * \return 842 | * A `ProtobufCEnumValue` object. 843 | * \retval NULL 844 | * If not found. 845 | */ 846 | PROTOBUF_C__API 847 | const ProtobufCEnumValue * 848 | protobuf_c_enum_descriptor_get_value( 849 | const ProtobufCEnumDescriptor *desc, 850 | int value); 851 | 852 | /** 853 | * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by 854 | * the name of the field. 855 | * 856 | * \param desc 857 | * The `ProtobufCMessageDescriptor` object. 858 | * \param name 859 | * The name of the field. 860 | * \return 861 | * A `ProtobufCFieldDescriptor` object. 862 | * \retval NULL 863 | * If not found or if the optimize_for = CODE_SIZE option was set. 864 | */ 865 | PROTOBUF_C__API 866 | const ProtobufCFieldDescriptor * 867 | protobuf_c_message_descriptor_get_field_by_name( 868 | const ProtobufCMessageDescriptor *desc, 869 | const char *name); 870 | 871 | /** 872 | * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by 873 | * the tag value of the field. 874 | * 875 | * \param desc 876 | * The `ProtobufCMessageDescriptor` object. 877 | * \param value 878 | * The tag value of the field. 879 | * \return 880 | * A `ProtobufCFieldDescriptor` object. 881 | * \retval NULL 882 | * If not found. 883 | */ 884 | PROTOBUF_C__API 885 | const ProtobufCFieldDescriptor * 886 | protobuf_c_message_descriptor_get_field( 887 | const ProtobufCMessageDescriptor *desc, 888 | unsigned value); 889 | 890 | /** 891 | * Determine the number of bytes required to store the serialised message. 892 | * 893 | * \param message 894 | * The message object to serialise. 895 | * \return 896 | * Number of bytes. 897 | */ 898 | PROTOBUF_C__API 899 | size_t 900 | protobuf_c_message_get_packed_size(const ProtobufCMessage *message); 901 | 902 | /** 903 | * Serialise a message from its in-memory representation. 904 | * 905 | * This function stores the serialised bytes of the message in a pre-allocated 906 | * buffer. 907 | * 908 | * \param message 909 | * The message object to serialise. 910 | * \param[out] out 911 | * Buffer to store the bytes of the serialised message. This buffer must 912 | * have enough space to store the packed message. Use 913 | * protobuf_c_message_get_packed_size() to determine the number of bytes 914 | * required. 915 | * \return 916 | * Number of bytes stored in `out`. 917 | */ 918 | PROTOBUF_C__API 919 | size_t 920 | protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out); 921 | 922 | /** 923 | * Serialise a message from its in-memory representation to a virtual buffer. 924 | * 925 | * This function calls the `append` method of a `ProtobufCBuffer` object to 926 | * consume the bytes generated by the serialiser. 927 | * 928 | * \param message 929 | * The message object to serialise. 930 | * \param buffer 931 | * The virtual buffer object. 932 | * \return 933 | * Number of bytes passed to the virtual buffer. 934 | */ 935 | PROTOBUF_C__API 936 | size_t 937 | protobuf_c_message_pack_to_buffer( 938 | const ProtobufCMessage *message, 939 | ProtobufCBuffer *buffer); 940 | 941 | /** 942 | * Unpack a serialised message into an in-memory representation. 943 | * 944 | * \param descriptor 945 | * The message descriptor. 946 | * \param allocator 947 | * `ProtobufCAllocator` to use for memory allocation. May be NULL to 948 | * specify the default allocator. 949 | * \param len 950 | * Length in bytes of the serialised message. 951 | * \param data 952 | * Pointer to the serialised message. 953 | * \return 954 | * An unpacked message object. 955 | * \retval NULL 956 | * If an error occurred during unpacking. 957 | */ 958 | PROTOBUF_C__API 959 | ProtobufCMessage * 960 | protobuf_c_message_unpack( 961 | const ProtobufCMessageDescriptor *descriptor, 962 | ProtobufCAllocator *allocator, 963 | size_t len, 964 | const uint8_t *data); 965 | 966 | /** 967 | * Free an unpacked message object. 968 | * 969 | * This function should be used to deallocate the memory used by a call to 970 | * protobuf_c_message_unpack(). 971 | * 972 | * \param message 973 | * The message object to free. May be NULL. 974 | * \param allocator 975 | * `ProtobufCAllocator` to use for memory deallocation. May be NULL to 976 | * specify the default allocator. 977 | */ 978 | PROTOBUF_C__API 979 | void 980 | protobuf_c_message_free_unpacked( 981 | ProtobufCMessage *message, 982 | ProtobufCAllocator *allocator); 983 | 984 | /** 985 | * Check the validity of a message object. 986 | * 987 | * Makes sure all required fields (`PROTOBUF_C_LABEL_REQUIRED`) are present. 988 | * Recursively checks nested messages. 989 | * 990 | * \retval TRUE 991 | * Message is valid. 992 | * \retval FALSE 993 | * Message is invalid. 994 | */ 995 | PROTOBUF_C__API 996 | protobuf_c_boolean 997 | protobuf_c_message_check(const ProtobufCMessage *); 998 | 999 | /** Message initialiser. */ 1000 | #define PROTOBUF_C_MESSAGE_INIT(descriptor) { descriptor, 0, NULL } 1001 | 1002 | /** 1003 | * Initialise a message object from a message descriptor. 1004 | * 1005 | * \param descriptor 1006 | * Message descriptor. 1007 | * \param message 1008 | * Allocated block of memory of size `descriptor->sizeof_message`. 1009 | */ 1010 | PROTOBUF_C__API 1011 | void 1012 | protobuf_c_message_init( 1013 | const ProtobufCMessageDescriptor *descriptor, 1014 | void *message); 1015 | 1016 | /** 1017 | * Free a service. 1018 | * 1019 | * \param service 1020 | * The service object to free. 1021 | */ 1022 | PROTOBUF_C__API 1023 | void 1024 | protobuf_c_service_destroy(ProtobufCService *service); 1025 | 1026 | /** 1027 | * Look up a `ProtobufCMethodDescriptor` by name. 1028 | * 1029 | * \param desc 1030 | * Service descriptor. 1031 | * \param name 1032 | * Name of the method. 1033 | * 1034 | * \return 1035 | * A `ProtobufCMethodDescriptor` object. 1036 | * \retval NULL 1037 | * If not found or if the optimize_for = CODE_SIZE option was set. 1038 | */ 1039 | PROTOBUF_C__API 1040 | const ProtobufCMethodDescriptor * 1041 | protobuf_c_service_descriptor_get_method_by_name( 1042 | const ProtobufCServiceDescriptor *desc, 1043 | const char *name); 1044 | 1045 | /** 1046 | * Initialise a `ProtobufCBufferSimple` object. 1047 | */ 1048 | #define PROTOBUF_C_BUFFER_SIMPLE_INIT(array_of_bytes) \ 1049 | { \ 1050 | { protobuf_c_buffer_simple_append }, \ 1051 | sizeof(array_of_bytes), \ 1052 | 0, \ 1053 | (array_of_bytes), \ 1054 | 0, \ 1055 | NULL \ 1056 | } 1057 | 1058 | /** 1059 | * Clear a `ProtobufCBufferSimple` object, freeing any allocated memory. 1060 | */ 1061 | #define PROTOBUF_C_BUFFER_SIMPLE_CLEAR(simp_buf) \ 1062 | do { \ 1063 | if ((simp_buf)->must_free_data) { \ 1064 | if ((simp_buf)->allocator != NULL) \ 1065 | (simp_buf)->allocator->free( \ 1066 | (simp_buf)->allocator, \ 1067 | (simp_buf)->data); \ 1068 | else \ 1069 | free((simp_buf)->data); \ 1070 | } \ 1071 | } while (0) 1072 | 1073 | /** 1074 | * The `append` method for `ProtobufCBufferSimple`. 1075 | * 1076 | * \param buffer 1077 | * The buffer object to append to. Must actually be a 1078 | * `ProtobufCBufferSimple` object. 1079 | * \param len 1080 | * Number of bytes in `data`. 1081 | * \param data 1082 | * Data to append. 1083 | */ 1084 | PROTOBUF_C__API 1085 | void 1086 | protobuf_c_buffer_simple_append( 1087 | ProtobufCBuffer *buffer, 1088 | size_t len, 1089 | const unsigned char *data); 1090 | 1091 | PROTOBUF_C__API 1092 | void 1093 | protobuf_c_service_generated_init( 1094 | ProtobufCService *service, 1095 | const ProtobufCServiceDescriptor *descriptor, 1096 | ProtobufCServiceDestroy destroy); 1097 | 1098 | PROTOBUF_C__API 1099 | void 1100 | protobuf_c_service_invoke_internal( 1101 | ProtobufCService *service, 1102 | unsigned method_index, 1103 | const ProtobufCMessage *input, 1104 | ProtobufCClosure closure, 1105 | void *closure_data); 1106 | 1107 | /**@}*/ 1108 | 1109 | PROTOBUF_C__END_DECLS 1110 | 1111 | #endif /* PROTOBUF_C_H */ 1112 | -------------------------------------------------------------------------------- /tests/run_test.sh: -------------------------------------------------------------------------------- 1 | gcc -DMT_USE_PTHREAD -lpthread -DMT_USE_NEON -o tensor.out tensor.c 2 | ./tensor.out 3 | rm -f tensor.out 4 | --------------------------------------------------------------------------------