Build a sorted "Label:count,Label:count" histogram of a result's def labels * (includes the always-emitted Module node). Deterministic + stable. */
| 24 | /* Build a sorted "Label:count,Label:count" histogram of a result's def labels |
| 25 | * (includes the always-emitted Module node). Deterministic + stable. */ |
| 26 | static void label_histogram(CBMFileResult *r, char *out, size_t out_sz) { |
| 27 | /* Collect distinct labels + counts (small N — linear scan is fine). */ |
| 28 | enum { MAXL = 32 }; |
| 29 | const char *labels[MAXL]; |
| 30 | int counts[MAXL]; |
| 31 | int nl = 0; |
| 32 | for (int i = 0; i < r->defs.count; i++) { |
| 33 | const char *l = r->defs.items[i].label ? r->defs.items[i].label : "(null)"; |
| 34 | int j = 0; |
| 35 | for (; j < nl; j++) { |
| 36 | if (strcmp(labels[j], l) == 0) { |
| 37 | counts[j]++; |
| 38 | break; |
| 39 | } |
| 40 | } |
| 41 | if (j == nl && nl < MAXL) { |
| 42 | labels[nl] = l; |
| 43 | counts[nl] = 1; |
| 44 | nl++; |
| 45 | } |
| 46 | } |
| 47 | /* Insertion sort labels alphabetically for a canonical string. */ |
| 48 | for (int i = 1; i < nl; i++) { |
| 49 | const char *lk = labels[i]; |
| 50 | int ck = counts[i]; |
| 51 | int j = i - 1; |
| 52 | while (j >= 0 && strcmp(labels[j], lk) > 0) { |
| 53 | labels[j + 1] = labels[j]; |
| 54 | counts[j + 1] = counts[j]; |
| 55 | j--; |
| 56 | } |
| 57 | labels[j + 1] = lk; |
| 58 | counts[j + 1] = ck; |
| 59 | } |
| 60 | out[0] = '\0'; |
| 61 | size_t used = 0; |
| 62 | for (int i = 0; i < nl; i++) { |
| 63 | int w = snprintf(out + used, out_sz - used, "%s%s:%d", i ? "," : "", labels[i], counts[i]); |
| 64 | if (w < 0 || (size_t)w >= out_sz - used) { |
| 65 | break; |
| 66 | } |
| 67 | used += (size_t)w; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | typedef struct { |
| 72 | const char *name; |