From 19199451c2251d3d9d036f6a3d061d8f36fc8295 Mon Sep 17 00:00:00 2001 From: Hazen Babcock Date: Tue, 8 Sep 2026 15:14:31 -0400 Subject: [PATCH 1/2] Use int32_t where Python passes an int32 array Fixes #27, which asked for int32_t instead of int "in many places". The places that matter are fewer than the phrasing suggests, and they are identifiable rather than a matter of taste. C to C calls do not care what sizeof(int) is, since the compiler is self-consistent, and a ctypes.c_int scalar matches a C int at any size. The assumption only bites where Python hands C a numpy int32 buffer and C reinterprets it as int[], because ctypes checks the dtype on the Python side only. A mismatch there is silent memory corruption, not an error. Scanning every *_c.py argtypes block against the C declarations, Python passes an int32 array to 24 functions. 14 already used int32_t, all of them in multi_fit.c and ia_utilities.c: the core was converted at some point and the rest was not, which is why the tree currently holds two conventions with no way to tell which is intended. This converts the remaining ten: frc/frc.c calc_frc frc_counts dbscan/dbscan.c dbscan db_cat, db_l locClSize cl_counts, cl_id recategorize cl_id, category L1H/homotopy_general.c getVisited vis L1H/homotopy_imagea_common.c getPeaks peak_c sa_library/grid.c grid2D grid, i_x, i_y grid3D grid, i_x, i_y, i_z sa_library/cs_decon_utilities.c label labels moments labels and adds stdint.h to the five files that lacked it, plus to homotopy_imagea_common.h, which now names int32_t in a declaration and is included by homotopy_imagea.c, which has no stdint.h of its own. None of the ten has a caller in C, so each change is a declaration and a definition with no ripple. The other 120 int* parameters in the tree never cross the ctypes boundary and are left alone. Note that there are two functions named getPeaks(). The one taking an int32 array is in L1H. decon_storm/mlem_sparse.c has another, whose int *n is an output count passed as byref(c_int()) and bound as c_void_p, so it is not a boundary case, and that file is not in SConstruct in any event. Nothing changes today, and provably so. int32_t is a typedef for int on every platform this package supports, LP64 and LLP64 alike, so compiling each file before and after gives byte identical objects: frc c32457ae2ebeeb8305c9cad368b656f1 dbscan f9900f35d6a712b50692d9b691d23862 homotopy_general decba0bc02496737904edeaa9e1a2c94 homotopy_imagea_common 6c85ec236b7ba856965b41a0c70f9f0e grid 01c7953b01aa98ad064b62127663a2c6 cs_decon_utilities 3cdb3093f179c5fe6f0aaeb92220d69d Both versions have to be compiled from the same filename for that to mean anything, since gcc embeds the source name. No new warnings; the two fread warnings in homotopy_imagea_common.c are pre-existing and appear identically before and after. That identity is also why the compiler cannot check this work: with int32_t and int the same type, a wrong conversion produces no diagnostic. The check is the scan, and it has to report every C definition of a name rather than the first one it finds, which is what originally hid the L1H getPeaks behind the decon_storm one. Note that dbscan.c has CRLF line endings, so it was edited in binary and its line ending count is unchanged. Co-Authored-By: Claude Opus 5 --- storm_analysis/L1H/homotopy_general.c | 3 ++- storm_analysis/L1H/homotopy_imagea_common.c | 3 ++- storm_analysis/L1H/homotopy_imagea_common.h | 4 +++- storm_analysis/dbscan/dbscan.c | 12 ++++++------ storm_analysis/frc/frc.c | 5 +++-- storm_analysis/sa_library/cs_decon_utilities.c | 9 +++++---- storm_analysis/sa_library/grid.c | 9 +++++---- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/storm_analysis/L1H/homotopy_general.c b/storm_analysis/L1H/homotopy_general.c index df47eed0..01d2555b 100644 --- a/storm_analysis/L1H/homotopy_general.c +++ b/storm_analysis/L1H/homotopy_general.c @@ -25,6 +25,7 @@ */ /* Include */ +#include #include #include #include @@ -300,7 +301,7 @@ void computeG3(void) * * vis - user supplied storage for the visited vector. */ -void getVisited(int *vis) +void getVisited(int32_t *vis) { int i; diff --git a/storm_analysis/L1H/homotopy_imagea_common.c b/storm_analysis/L1H/homotopy_imagea_common.c index 0cdfee16..e63f921f 100644 --- a/storm_analysis/L1H/homotopy_imagea_common.c +++ b/storm_analysis/L1H/homotopy_imagea_common.c @@ -5,6 +5,7 @@ */ /* Include */ +#include #include #include #include @@ -61,7 +62,7 @@ void finishUp(void) * * returns - number of peaks that were found. */ -int getPeaks(double *hres, double *peak_x, double *peak_y, double *peak_i, int *peak_c, int max_peaks) +int getPeaks(double *hres, double *peak_x, double *peak_y, double *peak_i, int32_t *peak_c, int max_peaks) { int i,j,n,o_size,t1; double xt,yt,t,inv_scale; diff --git a/storm_analysis/L1H/homotopy_imagea_common.h b/storm_analysis/L1H/homotopy_imagea_common.h index e7f17951..6a933dde 100644 --- a/storm_analysis/L1H/homotopy_imagea_common.h +++ b/storm_analysis/L1H/homotopy_imagea_common.h @@ -9,11 +9,13 @@ #ifndef HOMOTOPY_IMAGEA_COMMON_H #define HOMOTOPY_IMAGEA_COMMON_H +#include + /* Function Declarations */ void analyzeImage(double *, double *); void closeFile(void); void finishUp(void); -int getPeaks(double *, double *, double *, double *, int *, int); +int getPeaks(double *, double *, double *, double *, int32_t *, int); int openFile(char *); int saveHighRes(double *, int); void setImageParameters(double *, int, int, int, double, int, int, int, int, int); diff --git a/storm_analysis/dbscan/dbscan.c b/storm_analysis/dbscan/dbscan.c index 6f494b60..21fe3011 100644 --- a/storm_analysis/dbscan/dbscan.c +++ b/storm_analysis/dbscan/dbscan.c @@ -66,7 +66,7 @@ typedef struct // dbscan specific void appendN(intArr *, intArr *); intArr* createIntArr(int); -void dbscan(float *, float *, float *, int *, int *, int, float, int, int); +void dbscan(float *, float *, float *, int32_t *, int32_t *, int, float, int, int); int expandCluster(intArr *, int, float, int, int); void mergeN(intArr *, intArr *); intArr* regionQuery(int *, int, int, int, float); @@ -74,8 +74,8 @@ intArr* regionQueryKD(int *, int, int, int, float); // additional void clusterSize(int *, int *, int, int); -void locClSize(int *, int *, int, int); -void recategorize(int *, int *, int, int, int); +void locClSize(int32_t *, int32_t *, int, int); +void recategorize(int32_t *, int32_t *, int, int, int); /* Global Variables */ @@ -166,7 +166,7 @@ intArr* createIntArr(int n) * min_points - minimum number of points in a cluster. * verbose - print cluster information as we go. */ -void dbscan(float *db_x, float *db_y, float *db_z, int *db_cat, int *db_l, int db_nsize, float eps, int min_points, int verbose) +void dbscan(float *db_x, float *db_y, float *db_z, int32_t *db_cat, int32_t *db_l, int db_nsize, float eps, int min_points, int verbose) { int i,cluster_size,cn,counts,cur_cat; intArr *N; @@ -535,7 +535,7 @@ void clusterSize(int *counts, int *cl_id, int cl_size, int max_id) * cl_size - size of cl_counts (and cl_id). * max_id - maximum cluster id number. */ -void locClSize(int *cl_counts, int *cl_id, int cl_size, int max_id) +void locClSize(int32_t *cl_counts, int32_t *cl_id, int cl_size, int max_id) { int i; int *counts; @@ -562,7 +562,7 @@ void locClSize(int *cl_counts, int *cl_id, int cl_size, int max_id) * max_id - maximum cluster id number. * min_size - minimum acceptable cluster size. */ -void recategorize(int *cl_id, int *category, int cl_size, int max_id, int min_size) +void recategorize(int32_t *cl_id, int32_t *category, int cl_size, int max_id, int min_size) { int i; int *counts; diff --git a/storm_analysis/frc/frc.c b/storm_analysis/frc/frc.c index 77e62070..94630732 100644 --- a/storm_analysis/frc/frc.c +++ b/storm_analysis/frc/frc.c @@ -18,12 +18,13 @@ */ /* Include */ +#include #include #include #include /* Function Declarations */ -void calc_frc(double *, double *, double *, int *, int, int); +void calc_frc(double *, double *, double *, int32_t *, int, int); /* * calc_frc() @@ -42,7 +43,7 @@ void calc_frc(double *, double *, double *, int *, int, int); * y_size - Size of the FFT array in y (arr.shape[0]). * x_size - Size of the FFT array in x (arr.shape[1]). */ -void calc_frc(double *fft1, double *fft2, double *frc, int *frc_counts, int y_size, int x_size) +void calc_frc(double *fft1, double *fft2, double *frc, int32_t *frc_counts, int y_size, int x_size) { int cx,cy,dx,dy,i,j,q; int real,ima; diff --git a/storm_analysis/sa_library/cs_decon_utilities.c b/storm_analysis/sa_library/cs_decon_utilities.c index 63040f7e..4530fc94 100644 --- a/storm_analysis/sa_library/cs_decon_utilities.c +++ b/storm_analysis/sa_library/cs_decon_utilities.c @@ -9,14 +9,15 @@ */ /* Include */ +#include #include #include /* Function Declarations */ int isMaxima(double *, int, int, int, int, int, int); -int label(double *, int *, double, int, int, int, int); +int label(double *, int32_t *, double, int, int, int, int); void labelImage(double *, int *, double, int, int, int, int, int, int, int); -void moments(double *, double *, int *, int, int, int, int); +void moments(double *, double *, int32_t *, int, int, int, int); /* Functions */ @@ -83,7 +84,7 @@ int isMaxima(double *image, int ci, int cj, int ck, int x_size, int y_size, int * * Returns the number of unique labels. */ -int label(double *image, int *labels, double threshold, int margin, int x_size, int y_size, int z_size) +int label(double *image, int32_t *labels, double threshold, int margin, int x_size, int y_size, int z_size) { int i,j,k,t; int cur_label; @@ -170,7 +171,7 @@ void labelImage(double *image, int *labels, double threshold, int cur_label, int * y_size - Image size in y. * z_size - Image size in z. */ -void moments(double *image, double *peaks, int *labels, int counts, int x_size, int y_size, int z_size) +void moments(double *image, double *peaks, int32_t *labels, int counts, int x_size, int y_size, int z_size) { int i,j,k,l,t; diff --git a/storm_analysis/sa_library/grid.c b/storm_analysis/sa_library/grid.c index 308c301e..a3d143d8 100644 --- a/storm_analysis/sa_library/grid.c +++ b/storm_analysis/sa_library/grid.c @@ -18,11 +18,12 @@ */ /* Include */ +#include #include #include -void grid2D(int *, int *, int *, int, int, int); -void grid3D(int *, int *, int *, int *, int, int, int, int); +void grid2D(int32_t *, int32_t *, int32_t *, int, int, int); +void grid3D(int32_t *, int32_t *, int32_t *, int32_t *, int, int, int, int); void grid3DZInclusive(int *, int *, int *, int *, int, int, int, int); /* @@ -37,7 +38,7 @@ void grid3DZInclusive(int *, int *, int *, int *, int, int, int, int); * y_size - grid size in y. * n_x - number of x (and y) locations. */ -void grid2D(int *grid, int *i_x, int *i_y, int x_size, int y_size, int n_x) +void grid2D(int32_t *grid, int32_t *i_x, int32_t *i_y, int x_size, int y_size, int n_x) { int i,x,y; @@ -64,7 +65,7 @@ void grid2D(int *grid, int *i_x, int *i_y, int x_size, int y_size, int n_x) * z_size - grid size in z. * n_x - number of x (and y,z) locations. */ -void grid3D(int *grid, int *i_x, int *i_y, int *i_z, int x_size, int y_size, int z_size, int n_x) +void grid3D(int32_t *grid, int32_t *i_x, int32_t *i_y, int32_t *i_z, int x_size, int y_size, int z_size, int n_x) { int i,x,y,z; From 849e4fa3c2ed1727d2ae5bc2f1fadc0c91977c57 Mon Sep 17 00:00:00 2001 From: Hazen Babcock Date: Tue, 8 Sep 2026 15:16:27 -0400 Subject: [PATCH 2/2] Give grid3DZInclusive the name it is declared with grid.c declared grid3DZInclusive() and documented it under that name, then defined grid3DInclusize(). So the same function was declared without a definition and defined without a declaration at once, which is the pair of defects 413d2c12 and e9ffba5b fixed in multi_fit.h, here in a single function. Nothing calls it, in C or through ctypes. Two of the three occurrences say grid3DZInclusive, and Inclusize is not a word, so the definition is what was wrong. Wrong since 6a486010, the commit that put the file in its current location. Also converts its four array parameters to int32_t, matching grid2D() and grid3D() next to it after 76080b6a. Python does not call this one so it is not a boundary function today, but it takes the same arguments as its siblings, and leaving one function in the file on the old convention would recreate in miniature the split that commit set out to remove. This one cannot be verified by a byte identical object, since renaming a function changes its symbol. Instead, compiling before and after gives a symbol table differing in exactly one line, < 0000000000000160 T grid3DInclusize > 0000000000000160 T grid3DZInclusive at the same address, and a disassembly that is identical instruction for instruction once that one name is normalized. Co-Authored-By: Claude Opus 5 --- storm_analysis/sa_library/grid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storm_analysis/sa_library/grid.c b/storm_analysis/sa_library/grid.c index a3d143d8..a16a9f01 100644 --- a/storm_analysis/sa_library/grid.c +++ b/storm_analysis/sa_library/grid.c @@ -24,7 +24,7 @@ void grid2D(int32_t *, int32_t *, int32_t *, int, int, int); void grid3D(int32_t *, int32_t *, int32_t *, int32_t *, int, int, int, int); -void grid3DZInclusive(int *, int *, int *, int *, int, int, int, int); +void grid3DZInclusive(int32_t *, int32_t *, int32_t *, int32_t *, int, int, int, int); /* * grid2D() @@ -94,7 +94,7 @@ void grid3D(int32_t *grid, int32_t *i_x, int32_t *i_y, int32_t *i_z, int x_size, * z_size - grid size in z. * n_x - number of x (and y,z) locations. */ -void grid3DInclusize(int *grid, int *i_x, int *i_y, int *i_z, int x_size, int y_size, int z_size, int n_x) +void grid3DZInclusive(int32_t *grid, int32_t *i_x, int32_t *i_y, int32_t *i_z, int x_size, int y_size, int z_size, int n_x) { int i,x,y,z;