From 0aa10dd2dd615473da25598f6b6b9b7907bb23c5 Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Wed, 11 Jun 2025 16:52:24 +0530 Subject: [PATCH 01/10] Comments Sync API --- Include/Reai/Api.h | 147 +++++++++++ Include/Reai/Api/Types.h | 1 + Include/Reai/Api/Types/AnnFnMatch.h | 39 --- Include/Reai/Api/Types/Comment.h | 59 +++++ Include/Reai/Api/Types/Common.h | 6 + Include/Reai/Util/Vec.h | 60 ++--- Source/Reai/Api.c | 373 ++++++++++++++++++++++++++++ Source/Reai/Api/Types/AnnFnMatch.c | 42 ---- Source/Reai/Api/Types/Comment.c | 35 +++ 9 files changed, 653 insertions(+), 109 deletions(-) delete mode 100644 Include/Reai/Api/Types/AnnFnMatch.h create mode 100644 Include/Reai/Api/Types/Comment.h delete mode 100644 Source/Reai/Api/Types/AnnFnMatch.c create mode 100644 Source/Reai/Api/Types/Comment.c diff --git a/Include/Reai/Api.h b/Include/Reai/Api.h index e2e1d84..25aef48 100644 --- a/Include/Reai/Api.h +++ b/Include/Reai/Api.h @@ -492,6 +492,153 @@ extern "C" { Str* file_path ); + /// + /// GET: /v2/functions/{function_id}/decompilation/comments + /// + /// Get list of all comments created for function with given Id. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// + /// SUCCESS: A vector object containing all comments created by user. + /// FAILURE: Empty object. + /// + REAI_API Comments GetDecompilationComments (Connection* conn, FunctionId function_id); + + /// + /// POST: /v2/functions/{function_id}/decompilation/comments + /// + /// Create a new comment for decompilation for function with given function id. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment[in] : Str object containing the comment contents. + /// start_line[in] : Start line within decompilation to add comment to. + /// end_line[in] : End line within decompilation to add comment to. + /// + /// SUCCESS: A non-zero unique value associated with newly created comment, indicating comment creation was successful. + /// FAILURE: Zero. + /// + REAI_API CommentId AddDecompilationComment ( + Connection* conn, + FunctionId function_id, + Str* comment, + u64 start_line, + u64 end_line + ); + + /// + /// PATCH: /v2/functions/{function_id}/decompilation/comments/{comment_id} + /// + /// Update an existing comment. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment_id[in] : Comment ID for comment to be updated. + /// comment[in] : Str object containing the comment contents. + /// + /// SUCCESS: true indicating successful comment update. + /// FAILURE: false. + /// + REAI_API bool UpdateDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id, + Str* comment + ); + + /// + /// DELETE: /v2/functions/{function_id}/decompilation/comments/{comment_id} + /// + /// Delete an existing comment. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment_id[in] : Comment ID for comment to be updated. + /// + /// SUCCESS: true indicating successful comment deletion. + /// FAILURE: false. + /// + REAI_API bool + DeleteDecompilationComment (Connection* conn, FunctionId function_id, CommentId comment_id); + + /// + /// GET: /v2/functions/{function_id}/ai-decompilation/comments + /// + /// Get list of all comments created for function with given Id. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// + /// SUCCESS: A vector object containing all comments created by user. + /// FAILURE: Empty object. + /// + REAI_API Comments GetAiDecompilationComments (Connection* conn, FunctionId function_id); + + /// + /// POST: /v2/functions/{function_id}/ai-decompilation/comments + /// + /// Create a new comment for decompilation for function with given function id. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment[in] : Str object containing the comment contents. + /// start_line[in] : Start line within decompilation to add comment to. + /// end_line[in] : End line within decompilation to add comment to. + /// + /// SUCCESS: A non-zero unique value associated with newly created comment, indicating comment creation was successful. + /// FAILURE: Zero. + /// + REAI_API CommentId AddAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + Str* comment, + u64 start_line, + u64 end_line + ); + + /// + /// PATCH: /v2/functions/{function_id}/ai-decompilation/comments/{comment_id} + /// + /// Update an existing comment. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment_id[in] : Comment ID for comment to be updated. + /// comment[in] : Str object containing the comment contents. + /// + /// SUCCESS: true indicating successful comment update. + /// FAILURE: false. + /// + REAI_API bool UpdateAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id, + Str* comment + ); + + /// + /// DELETE: /v2/functions/{function_id}/ai-decompilation/comments/{comment_id} + /// + /// Delete an existing comment. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment_id[in] : Comment ID for comment to be updated. + /// + /// SUCCESS: true indicating successful comment deletion. + /// FAILURE: false. + /// + REAI_API bool DeleteAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id + ); + + // + // GET: /v2/users/me/comments + + #ifdef __cplusplus } #endif diff --git a/Include/Reai/Api/Types.h b/Include/Reai/Api/Types.h index 06c70dd..f3b2558 100644 --- a/Include/Reai/Api/Types.h +++ b/Include/Reai/Api/Types.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/Include/Reai/Api/Types/AnnFnMatch.h b/Include/Reai/Api/Types/AnnFnMatch.h deleted file mode 100644 index 34d3d9c..0000000 --- a/Include/Reai/Api/Types/AnnFnMatch.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file AnnFnMatch.h - * @date 2nd August 2024 - * @author Siddharth Mishra (admin@brightprogrammer.in) - * @copyright Copyright (c) RevEngAI. All Rights Reserved. - * */ - - -#ifndef REAI_ANN_FN_MATCH_H -#define REAI_ANN_FN_MATCH_H - -#include -#include -#include -#include - -typedef struct AnnFnMatch { - f64 confidence; - BinaryId binary_id; - Str binary_name; - bool debug; - FunctionId function_id; - Str function_name; - Str sha256; - FunctionId origin_function_id; -} AnnFnMatch; - -#ifdef __cplusplus -extern "C" { -#endif - - REAI_API void AnnFnMatchDeinit (AnnFnMatch* clone); - REAI_API bool AnnFnMatchInitClone (AnnFnMatch* dst, AnnFnMatch* src); - -#ifdef __cplusplus -} -#endif - -#endif // REAI_ANN_FN_MATCH_H diff --git a/Include/Reai/Api/Types/Comment.h b/Include/Reai/Api/Types/Comment.h new file mode 100644 index 0000000..a018791 --- /dev/null +++ b/Include/Reai/Api/Types/Comment.h @@ -0,0 +1,59 @@ +/** + * @file Comment.h + * @date 11th Jun 2025 + * @author Siddharth Mishra (admin@brightprogrammer.in) + * @copyright Copyright (c) RevEngAI. All Rights Reserved. + * */ + +#ifndef COMMENT_H +#define COMMENT_H + +#include +#include + +typedef struct { + Str content; + CommentId id; + UserId user_id; + Str resource_type; + ResourceId resource_id; + struct { + u32 start_line; + u32 end_line; + } context; + Str created_at; + Str updated_at; +} Comment; + +typedef Vec (Comment) Comments; + +#ifdef __cplusplus +extern "C" { +#endif + + /// + /// Init clone of object + /// Vectors in inited in clone will create new copies of data instead of sharing ownersip + /// + /// dst[out] : Destination of cloned data + /// src[in] : Cloning source + /// + /// SUCCESS : true + /// FAILURE : Does not return + /// + REAI_API bool CommentInitClone (Comment* dst, Comment* src); + + + /// + /// Deinit object + /// Must not be called manually if inside a vector that has deinit method set. + /// + /// dst[in] : Destination of cloned data + /// + REAI_API void CommentDeinit (Comment* c); + +#ifdef __cplusplus +} +#endif + +#endif // COMMENT_H diff --git a/Include/Reai/Api/Types/Common.h b/Include/Reai/Api/Types/Common.h index 5a69d27..99b25d5 100644 --- a/Include/Reai/Api/Types/Common.h +++ b/Include/Reai/Api/Types/Common.h @@ -13,6 +13,9 @@ typedef u64 FunctionId; typedef u64 CollectionId; typedef u64 ModelId; typedef u64 TeamId; +typedef u64 CommentId; +typedef u64 UserId; +typedef u64 ResourceId; typedef Vec (u64) IdsVec; @@ -22,5 +25,8 @@ typedef IdsVec FunctionIds; typedef IdsVec CollectionIds; typedef IdsVec ModelIds; typedef IdsVec TeamIds; +typedef IdsVec CommentIds; +typedef IdsVec UserIds; +typedef IdsVec ResourceIds; #endif // REAI_API_TYPES_COMMON_H diff --git a/Include/Reai/Util/Vec.h b/Include/Reai/Util/Vec.h index fd01a9e..73e31d0 100644 --- a/Include/Reai/Util/Vec.h +++ b/Include/Reai/Util/Vec.h @@ -82,13 +82,14 @@ typedef struct { /// #ifdef __cplusplus # define VecInit_T(v) \ - (TYPE_OF (*v \ - ) {.length = 0, \ - .capacity = 0, \ - .copy_init = (GenericCopyInit)NULL, \ - .copy_deinit = (GenericCopyDeinit)NULL, \ - .data = NULL, \ - .alignment = 1}) + (TYPE_OF (*v) { \ + .length = 0, \ + .capacity = 0, \ + .copy_init = (GenericCopyInit)NULL, \ + .copy_deinit = (GenericCopyDeinit)NULL, \ + .data = NULL, \ + .alignment = 1 \ + }) #else # define VecInit_T(v) \ ((TYPE_OF (*v)) {.length = 0, \ @@ -137,13 +138,14 @@ typedef struct { /// #ifdef __cplusplus # define VecInitWithDeepCopy_T(v, ci, cd) \ - (TYPE_OF (*v \ - ) {.length = 0, \ - .capacity = 0, \ - .copy_init = (GenericCopyInit)(ci), \ - .copy_deinit = (GenericCopyDeinit)(cd), \ - .data = NULL, \ - .alignment = 1}) + (TYPE_OF (*v) { \ + .length = 0, \ + .capacity = 0, \ + .copy_init = (GenericCopyInit)(ci), \ + .copy_deinit = (GenericCopyDeinit)(cd), \ + .data = NULL, \ + .alignment = 1 \ + }) #else # define VecInitWithDeepCopy_T(v, ci, cd) \ ((TYPE_OF (*v)) {.length = 0, \ @@ -200,13 +202,14 @@ typedef struct { /// #ifdef __cplusplus # define VecInitAligned_T(v, aln) \ - (TYPE_OF (*v \ - ) {.length = 0, \ - .capacity = 0, \ - .copy_init = (GenericCopyInit)NULL, \ - .copy_deinit = (GenericCopyDeinit)NULL, \ - .data = NULL, \ - .alignment = (aln)}) + (TYPE_OF (*v) { \ + .length = 0, \ + .capacity = 0, \ + .copy_init = (GenericCopyInit)NULL, \ + .copy_deinit = (GenericCopyDeinit)NULL, \ + .data = NULL, \ + .alignment = (aln) \ + }) #else # define VecInitAligned_T(v, aln) \ ((TYPE_OF (*v)) {.length = 0, \ @@ -272,13 +275,14 @@ typedef struct { /// #ifdef __cplusplus # define VecInitAlignedWithDeepCopy_T(v, ci, cd, aln) \ - (TYPE_OF (*v \ - ) {.length = 0, \ - .capacity = 0, \ - .copy_init = (GenericCopyInit)(ci), \ - .copy_deinit = (GenericCopyDeinit)(cd), \ - .data = NULL, \ - .alignment = (aln)}) + (TYPE_OF (*v) { \ + .length = 0, \ + .capacity = 0, \ + .copy_init = (GenericCopyInit)(ci), \ + .copy_deinit = (GenericCopyDeinit)(cd), \ + .data = NULL, \ + .alignment = (aln) \ + }) #else # define VecInitAlignedWithDeepCopy_T(v, ci, cd, aln) \ ((TYPE_OF (*v)) {.length = 0, \ diff --git a/Source/Reai/Api.c b/Source/Reai/Api.c index dc203f8..3c78dc5 100644 --- a/Source/Reai/Api.c +++ b/Source/Reai/Api.c @@ -1167,6 +1167,379 @@ Str UploadFile (Connection* conn, Str file_path) { } } +Comments GetDecompilationComments (Connection* conn, FunctionId function_id) { + if (!conn || !function_id) { + LOG_ERROR ("Invalid argument"); + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); + } + + Str url = StrInit(); + StrPrintf (&url, "%s/v2/functions/%llu/decompilation/comments", conn->host.data, function_id); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "POST")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + Comments comments = VecInitWithDeepCopy (NULL, CommentDeinit); + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_ARR_KV (j, "data", { + Comment c; + c.content = StrInit(); + c.created_at = StrInit(); + c.updated_at = StrInit(); + c.resource_type = StrInit(); + JR_OBJ (j, { + JR_STR_KV (j, "content", c.content); + JR_INT_KV (j, "id", c.id); + JR_INT_KV (j, "user_id", c.user_id); + JR_STR_KV (j, "resource_type", c.resource_type); + JR_INT_KV (j, "resource_id", c.resource_id); + JR_OBJ_KV (j, "context", { + JR_INT_KV (j, "start_line", c.context.start_line); + JR_INT_KV (j, "end_line", c.context.end_line); + }); + JR_STR_KV (j, "created_at", c.created_at); + JR_STR_KV (j, "updated_at", c.updated_at); + }); + VecPushBack (&comments, c); + }); + } + }); + + StrDeinit (&gj); + + return comments; + } + + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); +} + +CommentId AddDecompilationComment ( + Connection* conn, + FunctionId function_id, + Str* comment, + u64 start_line, + u64 end_line +) { + if (!conn || !function_id || !comment || start_line >= end_line) { + LOG_ERROR ("Invalid arguments"); + return 0; + } + + + Str url = StrInit(); + StrPrintf (&url, "%s/v2/functions/%llu/decompilation/comments", conn->host.data, function_id); + + Str sj = StrInit(); + StrPrintf ( + &sj, + "{\"content\":\"%s\", \"context\"{\"start_line\":%llu, \"end_line\":%llu}}", + comment->data, + start_line, + end_line + ); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "POST")) { + StrDeinit (&url); + StrDeinit (&sj); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + CommentId id = 0; + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_OBJ_KV (j, "data", { JR_INT_KV (j, "id", id); }); + } + }); + + StrDeinit (&gj); + + return id; + } + + return false; +} + +bool UpdateDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id, + Str* comment +) { + if (!conn || !function_id || !comment_id || !comment) { + LOG_ERROR ("Invalid arguments"); + return false; + } + + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/decompilation/comments/%llu", + conn->host.data, + function_id, + comment_id + ); + + Str sj = StrInit(); + StrPrintf (&sj, "{\"content\":\"%s\"}", comment->data); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "DELETE")) { + StrDeinit (&url); + StrDeinit (&sj); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + JR_OBJ (j, { JR_BOOL_KV (j, "status", status); }); + + StrDeinit (&gj); + + return status; + } + + return false; +} + +bool DeleteDecompilationComment (Connection* conn, FunctionId function_id, CommentId comment_id) { + if (!conn || !function_id || !comment_id) { + LOG_ERROR ("Invalid arguments"); + return false; + } + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/decompilation/comments/%llu", + conn->host.data, + function_id, + comment_id + ); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "DELETE")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + bool data = false; + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_BOOL_KV (j, "data", data); + } + }); + + StrDeinit (&gj); + + return data; + } + + return false; +} + +Comments GetAiDecompilationComments (Connection* conn, FunctionId function_id) { + if (!conn || !function_id) { + LOG_ERROR ("Invalid argument"); + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); + } + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/ai-decompilation/comments", + conn->host.data, + function_id + ); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "POST")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + Comments comments = VecInitWithDeepCopy (NULL, CommentDeinit); + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_ARR_KV (j, "data", { + Comment c; + c.content = StrInit(); + c.created_at = StrInit(); + c.updated_at = StrInit(); + c.resource_type = StrInit(); + JR_OBJ (j, { + JR_STR_KV (j, "content", c.content); + JR_INT_KV (j, "id", c.id); + JR_INT_KV (j, "user_id", c.user_id); + JR_STR_KV (j, "resource_type", c.resource_type); + JR_INT_KV (j, "resource_id", c.resource_id); + JR_OBJ_KV (j, "context", { + JR_INT_KV (j, "start_line", c.context.start_line); + JR_INT_KV (j, "end_line", c.context.end_line); + }); + JR_STR_KV (j, "created_at", c.created_at); + JR_STR_KV (j, "updated_at", c.updated_at); + }); + VecPushBack (&comments, c); + }); + } + }); + + StrDeinit (&gj); + + return comments; + } + + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); +} + +CommentId AddAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + Str* comment, + u64 start_line, + u64 end_line +) { + if (!conn || !function_id || !comment || start_line >= end_line) { + LOG_ERROR ("Invalid arguments"); + return 0; + } + + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/ai-decompilation/comments", + conn->host.data, + function_id + ); + + Str sj = StrInit(); + StrPrintf ( + &sj, + "{\"content\":\"%s\", \"context\"{\"start_line\":%llu, \"end_line\":%llu}}", + comment->data, + start_line, + end_line + ); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "POST")) { + StrDeinit (&url); + StrDeinit (&sj); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + CommentId id = 0; + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_OBJ_KV (j, "data", { JR_INT_KV (j, "id", id); }); + } + }); + + StrDeinit (&gj); + + return id; + } + + return false; +} + +bool UpdateAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id, + Str* comment +) { + if (!conn || !function_id || !comment_id || !comment) { + LOG_ERROR ("Invalid arguments"); + return false; + } + + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/ai-decompilation/comments/%llu", + conn->host.data, + function_id, + comment_id + ); + + Str sj = StrInit(); + StrPrintf (&sj, "{\"content\":\"%s\"}", comment->data); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "DELETE")) { + StrDeinit (&url); + StrDeinit (&sj); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + JR_OBJ (j, { JR_BOOL_KV (j, "status", status); }); + + StrDeinit (&gj); + + return status; + } + + return false; +} + +bool DeleteAiDecompilationComment (Connection* conn, FunctionId function_id, CommentId comment_id) { + if (!conn || !function_id || !comment_id) { + LOG_ERROR ("Invalid arguments"); + return false; + } + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/ai-decompilation/comments/%llu", + conn->host.data, + function_id, + comment_id + ); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "DELETE")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + bool data = false; + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_BOOL_KV (j, "data", data); + } + }); + + StrDeinit (&gj); + + return data; + } + + return false; +} + + Str* UrlAddQueryStr (Str* url, const char* key, const char* value, bool* is_first) { if (!url || !key) { LOG_ERROR ("Invalid arguments."); diff --git a/Source/Reai/Api/Types/AnnFnMatch.c b/Source/Reai/Api/Types/AnnFnMatch.c deleted file mode 100644 index f5db61c..0000000 --- a/Source/Reai/Api/Types/AnnFnMatch.c +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file AnnFnMatch.c - * @date 20 August 2024 - * @author Siddharth Mishra (admin@brightprogrammer.in) - * @copyright Copyright (c) RevEngAI. All Rights Reserved. - * */ - -#include -#include - -/* libc */ -#include - -void AnnFnMatchDeinit (AnnFnMatch* afn) { - if (!afn) { - LOG_FATAL ("Invalid arguments. Aborting..."); - } - - StrDeinit (&afn->binary_name); - StrDeinit (&afn->function_name); - StrDeinit (&afn->sha256); - - memset (afn, 0, sizeof (AnnFnMatch)); -} - -AnnFnMatch* ann_fn_match_clone_init (AnnFnMatch* dst, AnnFnMatch* src) { - if (!dst || !src) { - LOG_FATAL ("Invalid arguments. Aborting..."); - } - - StrInitCopy (&dst->binary_name, &src->binary_name); - StrInitCopy (&dst->function_name, &src->function_name); - StrInitCopy (&dst->sha256, &src->sha256); - - dst->confidence = src->confidence; - dst->binary_id = src->binary_id; - dst->debug = src->debug; - dst->function_id = src->function_id; - dst->origin_function_id = src->origin_function_id; - - return dst; -} diff --git a/Source/Reai/Api/Types/Comment.c b/Source/Reai/Api/Types/Comment.c new file mode 100644 index 0000000..363e374 --- /dev/null +++ b/Source/Reai/Api/Types/Comment.c @@ -0,0 +1,35 @@ +#include +#include + +#include "Reai/Util/Str.h" + +bool CommentInitClone (Comment *dst, Comment *src) { + if (!dst || !src) { + LOG_FATAL ("Invalid comments object provided."); + } + + dst->content = StrDup (&src->content); + dst->id = src->id; + dst->user_id = src->user_id; + dst->resource_type = StrDup (&src->resource_type); + dst->resource_id = src->resource_id; + dst->context.start_line = src->context.start_line; + dst->context.end_line = src->context.end_line; + dst->created_at = StrDup (&src->created_at); + dst->updated_at = StrDup (&src->updated_at); + + return true; +} + +void CommentDeinit (Comment *c) { + if (!c) { + LOG_FATAL ("Invalid comment object provided"); + } + + StrDeinit (&c->content); + StrDeinit (&c->resource_type); + StrDeinit (&c->updated_at); + StrDeinit (&c->updated_at); + + memset (c, 0, sizeof (Comment)); +} From 8de073eafee8ca8055d4a3508004ead49d7c0d11 Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Wed, 11 Jun 2025 17:04:48 +0530 Subject: [PATCH 02/10] Get All User Comments --- Include/Reai/Api.h | 13 ++++++++++-- Source/Reai/Api.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/Include/Reai/Api.h b/Include/Reai/Api.h index 25aef48..ab24f47 100644 --- a/Include/Reai/Api.h +++ b/Include/Reai/Api.h @@ -635,9 +635,18 @@ extern "C" { CommentId comment_id ); - // - // GET: /v2/users/me/comments + /// + /// GET: /v2/users/me/comments + /// + /// Get all comments created by this user. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// + /// SUCCESS: Comments vector filled with information about all created comments. + /// FAILURE: Empty vector object. + /// + REAI_API Comments GetComments (Connection* conn); #ifdef __cplusplus } diff --git a/Source/Reai/Api.c b/Source/Reai/Api.c index 3c78dc5..665e05f 100644 --- a/Source/Reai/Api.c +++ b/Source/Reai/Api.c @@ -1539,6 +1539,58 @@ bool DeleteAiDecompilationComment (Connection* conn, FunctionId function_id, Com return false; } +REAI_API Comments GetComments (Connection* conn) { + if (!conn) { + LOG_ERROR ("Invalid argument"); + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); + } + + Str url = StrInit(); + StrPrintf (&url, "%s/v2/users/me/comments", conn->host.data); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "POST")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + Comments comments = VecInitWithDeepCopy (NULL, CommentDeinit); + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_ARR_KV (j, "data", { + Comment c; + c.content = StrInit(); + c.created_at = StrInit(); + c.updated_at = StrInit(); + c.resource_type = StrInit(); + JR_OBJ (j, { + JR_STR_KV (j, "content", c.content); + JR_INT_KV (j, "id", c.id); + JR_INT_KV (j, "user_id", c.user_id); + JR_STR_KV (j, "resource_type", c.resource_type); + JR_INT_KV (j, "resource_id", c.resource_id); + JR_OBJ_KV (j, "context", { + JR_INT_KV (j, "start_line", c.context.start_line); + JR_INT_KV (j, "end_line", c.context.end_line); + }); + JR_STR_KV (j, "created_at", c.created_at); + JR_STR_KV (j, "updated_at", c.updated_at); + }); + VecPushBack (&comments, c); + }); + } + }); + + StrDeinit (&gj); + + return comments; + } + + + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); +} Str* UrlAddQueryStr (Str* url, const char* key, const char* value, bool* is_first) { if (!url || !key) { From 67118cae082e09008d9abaa8b6ee22a974717c15 Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Thu, 12 Jun 2025 14:54:29 +0530 Subject: [PATCH 03/10] Use strncmp Instead of memcmp For String Compare --- Include/Reai/Util/Str.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/Reai/Util/Str.h b/Include/Reai/Util/Str.h index 591cccf..a4226ff 100644 --- a/Include/Reai/Util/Str.h +++ b/Include/Reai/Util/Str.h @@ -121,7 +121,7 @@ extern "C" { /// RETURN : +ve or -ve depending on above or below in lexical ordering /// RETURN : 0 if both are equal /// -#define StrCmp(str, ostr) memcmp ((str)->data, (ostr)->data, MIN2 ((str)->length, (ostr)->length)) +#define StrCmp(str, ostr) strncmp ((str)->data, (ostr)->data, (ostr)->length) /// /// Compare string with another const char* From ed3d583c11de493d18886e48cfa89e1b6f6a3b4f Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Thu, 12 Jun 2025 23:18:26 +0530 Subject: [PATCH 04/10] A Basic Working Diff Algorithm --- Include/Reai/Diff.h | 89 +++++++++++++++ Source/Reai/Diff.c | 244 +++++++++++++++++++++++++++++++++++++++++ Source/Reai/Util/Vec.c | 1 - 3 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 Include/Reai/Diff.h create mode 100644 Source/Reai/Diff.c diff --git a/Include/Reai/Diff.h b/Include/Reai/Diff.h new file mode 100644 index 0000000..e2870a0 --- /dev/null +++ b/Include/Reai/Diff.h @@ -0,0 +1,89 @@ +#ifndef REAI_DIFF_H +#define REAI_DIFF_H + +#include + +typedef enum { + DIFF_TYPE_SAM = 's', + DIFF_TYPE_ADD = '+', + DIFF_TYPE_REM = '-', + DIFF_TYPE_MOD = 'a', + DIFF_TYPE_MOV = 'm' +} DiffType; + +typedef struct { + DiffType type; + union { + struct { + u64 line; + Str content; + } sam, add, rem; + struct { + u64 old_line; + u64 new_line; + Str old_content; + Str new_content; + } mov, mod; + }; +} DiffLine; + +typedef Vec (DiffLine) DiffLines; + +#ifdef __cplusplus +# define DiffLineInitSam(l, s) \ + (DiffLine {.type = DIFF_TYPE_SAM, .sam.line = (l), .sam.content = StrDup (s)}) +# define DiffLineInitAdd(l, s) \ + (DiffLine {.type = DIFF_TYPE_ADD, .add.line = (l), .add.content = StrDup (s)}) +# define DiffLineInitRem(l, s) \ + (DiffLine {.type = DIFF_TYPE_REM, .rem.line = (l), .rem.content = StrDup (s)}) +# define DiffLineInitMov(lo, so, ln, sn) \ + (DiffLine { \ + .type = DIFF_TYPE_MOV, \ + .mov.old_line = (lo), \ + .mov.new_line = (ln), \ + .mov.old_content = StrDup (so), \ + .mov.new_content = StrDup (sn) \ + }) +# define DiffLineInitMod(lo, so, ln, sn) \ + (DiffLine { \ + .type = DIFF_TYPE_MOD, \ + .mov.old_line = (lo), \ + .mov.new_line = (ln), \ + .mov.old_content = StrDup (so), \ + .mov.new_content = StrDup (sn) \ + }) +#else +# define DiffLineInitSam(l, s) \ + ((DiffLine) {.type = DIFF_TYPE_SAM, .sam.line = (l), .sam.content = StrDup (s)}) +# define DiffLineInitAdd(l, s) \ + ((DiffLine) {.type = DIFF_TYPE_ADD, .add.line = (l), .add.content = StrDup (s)}) +# define DiffLineInitRem(l, s) \ + ((DiffLine) {.type = DIFF_TYPE_REM, .rem.line = (l), .rem.content = StrDup (s)}) +# define DiffLineInitMov(lo, so, ln, sn) \ + ((DiffLine) {.type = DIFF_TYPE_MOV, \ + .mov.old_line = (lo), \ + .mov.new_line = (ln), \ + .mov.old_content = StrDup (so), \ + .mov.new_content = StrDup (sn)}) +# define DiffLineInitMod(lo, so, ln, sn) \ + ((DiffLine) {.type = DIFF_TYPE_MOD, \ + .mov.old_line = (lo), \ + .mov.new_line = (ln), \ + .mov.old_content = StrDup (so), \ + .mov.new_content = StrDup (sn)}) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + REAI_API bool DiffLineInitClone (DiffLine* dst, DiffLine* src); + REAI_API void DiffLineDeinit (DiffLine* dl); + + REAI_API DiffLines GetDiff (Str* og, Str* nw); + +#ifdef __cplusplus +} +#endif + +#endif // REAI_DIFF_H diff --git a/Source/Reai/Diff.c b/Source/Reai/Diff.c new file mode 100644 index 0000000..1c98b99 --- /dev/null +++ b/Source/Reai/Diff.c @@ -0,0 +1,244 @@ +#include +#include + +#include "Reai/Util/Str.h" +#include "Reai/Util/Vec.h" + +bool DiffLineInitClone (DiffLine* dst, DiffLine* src) { + if (!dst || !src) { + LOG_FATAL ("Invalid arguments."); + } + + dst->type = src->type; + switch (dst->type) { + case DIFF_TYPE_ADD : + case DIFF_TYPE_REM : + case DIFF_TYPE_SAM : { + dst->add.line = src->add.line; + dst->add.content = StrDup (&src->add.content); + break; + } + + case DIFF_TYPE_MOD : + case DIFF_TYPE_MOV : { + dst->mov.new_line = src->mov.new_line; + dst->mov.old_line = src->mov.old_line; + dst->mov.new_content = StrDup (&src->mov.new_content); + dst->mov.old_content = StrDup (&src->mov.old_content); + break; + } + + default : { + LOG_FATAL ("Unreachable code reached : Invalid diff line type."); + } + } + + return true; +} + +void DiffLineDeinit (DiffLine* dl) { + if (!dl) { + LOG_FATAL ("Invalid argument"); + } + + switch (dl->type) { + case DIFF_TYPE_ADD : + case DIFF_TYPE_REM : + case DIFF_TYPE_SAM : { + StrDeinit (&dl->add.content); + dl->add.line = 0; + break; + } + + case DIFF_TYPE_MOV : + case DIFF_TYPE_MOD : { + StrDeinit (&dl->mod.new_content); + StrDeinit (&dl->mod.old_content); + dl->mod.new_line = 0; + dl->mod.old_line = 0; + break; + } + + default : { + LOG_FATAL ("Unreachable code reached. Invalid diff line type."); + } + } + + dl->type = 0; +} + +int DiffLineNumberCompare(const void* a, const void* b) { + const DiffLine* line_a = (const DiffLine*)a; + const DiffLine* line_b = (const DiffLine*)b; + + u64 line_a_num, line_b_num; + + // Get the primary line number for sorting (using old/source line numbers) + switch (line_a->type) { + case DIFF_TYPE_SAM: + case DIFF_TYPE_REM: + line_a_num = line_a->sam.line; + break; + case DIFF_TYPE_ADD: + line_a_num = line_a->add.line + 1000000; // Sort additions after source lines + break; + case DIFF_TYPE_MOV: + case DIFF_TYPE_MOD: + line_a_num = line_a->mov.old_line; + break; + default: + line_a_num = 0; + } + + switch (line_b->type) { + case DIFF_TYPE_SAM: + case DIFF_TYPE_REM: + line_b_num = line_b->sam.line; + break; + case DIFF_TYPE_ADD: + line_b_num = line_b->add.line + 1000000; // Sort additions after source lines + break; + case DIFF_TYPE_MOV: + case DIFF_TYPE_MOD: + line_b_num = line_b->mov.old_line; + break; + default: + line_b_num = 0; + } + + if (line_a_num < line_b_num) return -1; + if (line_a_num > line_b_num) return 1; + return 0; +} + +bool StrFuzzyCmp (Str* s1, Str* s2, u32 error_tolerance) { + if (!s1 || !s2) { + LOG_FATAL ("Invalid arguments"); + } + + u64 l1 = s1->length; + u64 l2 = s2->length; + const char* p1 = s1->data; + const char* p2 = s2->data; + + u64 length_diff = MAX2 (l1, l2) - MIN2 (l1, l2); + if (length_diff > error_tolerance) { + return false; + } else { + error_tolerance -= length_diff; + } + + u64 l = MIN2 (l1, l2); + while (l-- && *p1 && *p2) { + if (*p1 != *p2) { + if (!error_tolerance) { + return false; + } + error_tolerance--; + } + } + + return true; +} + +DiffLines GetDiff (Str* og, Str* nw) { + if (!og || !nw) { + LOG_FATAL ("Invalid arguments"); + } + + DiffLines diff = VecInitWithDeepCopy (NULL, DiffLineDeinit); + + Strs og_lines = StrSplit (og, "\n"); + Strs nw_lines = StrSplit (nw, "\n"); + + // Create tracking vectors to mark which lines have been matched + Vec(bool) og_matched = VecInit(); + Vec(bool) nw_matched = VecInit(); + + // Resize vectors to match the number of lines and initialize to false + VecResize(&og_matched, og_lines.length); + VecResize(&nw_matched, nw_lines.length); + + // Initialize all elements to false (VecResize should zero-initialize) + for (size_t i = 0; i < og_lines.length; i++) { + VecAt(&og_matched, i) = false; + } + for (size_t i = 0; i < nw_lines.length; i++) { + VecAt(&nw_matched, i) = false; + } + + // Phase 1: Find exact matches in the same positions + for (size_t i = 0; i < og_lines.length && i < nw_lines.length; i++) { + if (VecAt(&og_matched, i) || VecAt(&nw_matched, i)) continue; + + if (StrCmp(&og_lines.data[i], &nw_lines.data[i]) == 0) { + DiffLine same = DiffLineInitSam(i, &og_lines.data[i]); + VecPushBack(&diff, same); + VecAt(&og_matched, i) = true; + VecAt(&nw_matched, i) = true; + } + } + + // Phase 2: Find moves (exact content in different positions) + for (size_t og_idx = 0; og_idx < og_lines.length; og_idx++) { + if (VecAt(&og_matched, og_idx)) continue; + + for (size_t nw_idx = 0; nw_idx < nw_lines.length; nw_idx++) { + if (VecAt(&nw_matched, nw_idx)) continue; + + if (StrCmp(&og_lines.data[og_idx], &nw_lines.data[nw_idx]) == 0) { + DiffLine move = DiffLineInitMov(og_idx, &og_lines.data[og_idx], nw_idx, &nw_lines.data[nw_idx]); + VecPushBack(&diff, move); + VecAt(&og_matched, og_idx) = true; + VecAt(&nw_matched, nw_idx) = true; + break; + } + } + } + + // Phase 3: Find modifications using fuzzy matching + for (size_t og_idx = 0; og_idx < og_lines.length; og_idx++) { + if (VecAt(&og_matched, og_idx)) continue; + + for (size_t nw_idx = 0; nw_idx < nw_lines.length; nw_idx++) { + if (VecAt(&nw_matched, nw_idx)) continue; + + // Calculate fuzzy match tolerance (75% of the longer string) + u32 tolerance = (MAX2(og_lines.data[og_idx].length, nw_lines.data[nw_idx].length) * 3) / 4; + + if (StrFuzzyCmp(&og_lines.data[og_idx], &nw_lines.data[nw_idx], tolerance)) { + DiffLine mod = DiffLineInitMod(og_idx, &og_lines.data[og_idx], nw_idx, &nw_lines.data[nw_idx]); + VecPushBack(&diff, mod); + VecAt(&og_matched, og_idx) = true; + VecAt(&nw_matched, nw_idx) = true; + break; + } + } + } + + // Phase 4: Handle remaining unmatched lines (removals and additions) + for (size_t og_idx = 0; og_idx < og_lines.length; og_idx++) { + if (!VecAt(&og_matched, og_idx)) { + DiffLine removed = DiffLineInitRem(og_idx, &og_lines.data[og_idx]); + VecPushBack(&diff, removed); + } + } + + for (size_t nw_idx = 0; nw_idx < nw_lines.length; nw_idx++) { + if (!VecAt(&nw_matched, nw_idx)) { + DiffLine added = DiffLineInitAdd(nw_idx, &nw_lines.data[nw_idx]); + VecPushBack(&diff, added); + } + } + + // Phase 5: Sort the diff results by line number for proper output order + VecSort(&diff, DiffLineNumberCompare); + + // Cleanup + VecDeinit(&og_matched); + VecDeinit(&nw_matched); + VecDeinit(&og_lines); + VecDeinit(&nw_lines); + + return diff; +} diff --git a/Source/Reai/Util/Vec.c b/Source/Reai/Util/Vec.c index 5e269e7..0071678 100644 --- a/Source/Reai/Util/Vec.c +++ b/Source/Reai/Util/Vec.c @@ -53,7 +53,6 @@ void deinit_vec (GenericVec *vec, size item_size) { free (vec->data); } - memset (vec, 0, sizeof (GenericVec)); vec->data = NULL; vec->length = vec->capacity = 0; } From 22e9f7c811be562a336f1349f40a4943778f9f18 Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Thu, 12 Jun 2025 23:29:05 +0530 Subject: [PATCH 05/10] Use Standard Macros/Functions Wherever Possible --- Source/Reai/Diff.c | 55 +++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/Source/Reai/Diff.c b/Source/Reai/Diff.c index 1c98b99..76f4bce 100644 --- a/Source/Reai/Diff.c +++ b/Source/Reai/Diff.c @@ -158,21 +158,12 @@ DiffLines GetDiff (Str* og, Str* nw) { // Resize vectors to match the number of lines and initialize to false VecResize(&og_matched, og_lines.length); VecResize(&nw_matched, nw_lines.length); - - // Initialize all elements to false (VecResize should zero-initialize) - for (size_t i = 0; i < og_lines.length; i++) { - VecAt(&og_matched, i) = false; - } - for (size_t i = 0; i < nw_lines.length; i++) { - VecAt(&nw_matched, i) = false; - } // Phase 1: Find exact matches in the same positions - for (size_t i = 0; i < og_lines.length && i < nw_lines.length; i++) { - if (VecAt(&og_matched, i) || VecAt(&nw_matched, i)) continue; - - if (StrCmp(&og_lines.data[i], &nw_lines.data[i]) == 0) { - DiffLine same = DiffLineInitSam(i, &og_lines.data[i]); + size_t min_length = og_lines.length < nw_lines.length ? og_lines.length : nw_lines.length; + for (size_t i = 0; i < min_length; i++) { + if (StrCmp(VecPtrAt(&og_lines, i), VecPtrAt(&nw_lines, i)) == 0) { + DiffLine same = DiffLineInitSam(i, VecPtrAt(&og_lines, i)); VecPushBack(&diff, same); VecAt(&og_matched, i) = true; VecAt(&nw_matched, i) = true; @@ -180,56 +171,56 @@ DiffLines GetDiff (Str* og, Str* nw) { } // Phase 2: Find moves (exact content in different positions) - for (size_t og_idx = 0; og_idx < og_lines.length; og_idx++) { + VecForeachPtrIdx(&og_lines, og_line, og_idx, { if (VecAt(&og_matched, og_idx)) continue; - for (size_t nw_idx = 0; nw_idx < nw_lines.length; nw_idx++) { + VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { if (VecAt(&nw_matched, nw_idx)) continue; - if (StrCmp(&og_lines.data[og_idx], &nw_lines.data[nw_idx]) == 0) { - DiffLine move = DiffLineInitMov(og_idx, &og_lines.data[og_idx], nw_idx, &nw_lines.data[nw_idx]); + if (StrCmp(og_line, nw_line) == 0) { + DiffLine move = DiffLineInitMov(og_idx, og_line, nw_idx, nw_line); VecPushBack(&diff, move); VecAt(&og_matched, og_idx) = true; VecAt(&nw_matched, nw_idx) = true; break; } - } - } + }); + }); // Phase 3: Find modifications using fuzzy matching - for (size_t og_idx = 0; og_idx < og_lines.length; og_idx++) { + VecForeachPtrIdx(&og_lines, og_line, og_idx, { if (VecAt(&og_matched, og_idx)) continue; - for (size_t nw_idx = 0; nw_idx < nw_lines.length; nw_idx++) { + VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { if (VecAt(&nw_matched, nw_idx)) continue; // Calculate fuzzy match tolerance (75% of the longer string) - u32 tolerance = (MAX2(og_lines.data[og_idx].length, nw_lines.data[nw_idx].length) * 3) / 4; + u32 tolerance = (MAX2(og_line->length, nw_line->length) * 3) / 4; - if (StrFuzzyCmp(&og_lines.data[og_idx], &nw_lines.data[nw_idx], tolerance)) { - DiffLine mod = DiffLineInitMod(og_idx, &og_lines.data[og_idx], nw_idx, &nw_lines.data[nw_idx]); + if (StrFuzzyCmp(og_line, nw_line, tolerance)) { + DiffLine mod = DiffLineInitMod(og_idx, og_line, nw_idx, nw_line); VecPushBack(&diff, mod); VecAt(&og_matched, og_idx) = true; VecAt(&nw_matched, nw_idx) = true; break; } - } - } + }); + }); // Phase 4: Handle remaining unmatched lines (removals and additions) - for (size_t og_idx = 0; og_idx < og_lines.length; og_idx++) { + VecForeachPtrIdx(&og_lines, og_line, og_idx, { if (!VecAt(&og_matched, og_idx)) { - DiffLine removed = DiffLineInitRem(og_idx, &og_lines.data[og_idx]); + DiffLine removed = DiffLineInitRem(og_idx, og_line); VecPushBack(&diff, removed); } - } + }); - for (size_t nw_idx = 0; nw_idx < nw_lines.length; nw_idx++) { + VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { if (!VecAt(&nw_matched, nw_idx)) { - DiffLine added = DiffLineInitAdd(nw_idx, &nw_lines.data[nw_idx]); + DiffLine added = DiffLineInitAdd(nw_idx, nw_line); VecPushBack(&diff, added); } - } + }); // Phase 5: Sort the diff results by line number for proper output order VecSort(&diff, DiffLineNumberCompare); From 01856f6cafae7bf93fd49e5747813d56b1629fd9 Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Thu, 12 Jun 2025 23:35:54 +0530 Subject: [PATCH 06/10] 50% Tolerance Level --- Source/Reai/Diff.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/Source/Reai/Diff.c b/Source/Reai/Diff.c index 76f4bce..aafcfb8 100644 --- a/Source/Reai/Diff.c +++ b/Source/Reai/Diff.c @@ -170,44 +170,45 @@ DiffLines GetDiff (Str* og, Str* nw) { } } - // Phase 2: Find moves (exact content in different positions) + // Phase 2: Find moves and modifications in a single pass VecForeachPtrIdx(&og_lines, og_line, og_idx, { if (VecAt(&og_matched, og_idx)) continue; + bool found_match = false; VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { if (VecAt(&nw_matched, nw_idx)) continue; + // First check for exact match (move) if (StrCmp(og_line, nw_line) == 0) { DiffLine move = DiffLineInitMov(og_idx, og_line, nw_idx, nw_line); VecPushBack(&diff, move); VecAt(&og_matched, og_idx) = true; VecAt(&nw_matched, nw_idx) = true; + found_match = true; break; } }); - }); - - // Phase 3: Find modifications using fuzzy matching - VecForeachPtrIdx(&og_lines, og_line, og_idx, { - if (VecAt(&og_matched, og_idx)) continue; - VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { - if (VecAt(&nw_matched, nw_idx)) continue; - - // Calculate fuzzy match tolerance (75% of the longer string) - u32 tolerance = (MAX2(og_line->length, nw_line->length) * 3) / 4; - - if (StrFuzzyCmp(og_line, nw_line, tolerance)) { - DiffLine mod = DiffLineInitMod(og_idx, og_line, nw_idx, nw_line); - VecPushBack(&diff, mod); - VecAt(&og_matched, og_idx) = true; - VecAt(&nw_matched, nw_idx) = true; - break; - } - }); + // If no exact match found, look for fuzzy match (modification) + if (!found_match) { + VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { + if (VecAt(&nw_matched, nw_idx)) continue; + + // Calculate fuzzy match tolerance (50% of the longer string) + u32 tolerance = MAX2(og_line->length, nw_line->length) / 2; + + if (StrFuzzyCmp(og_line, nw_line, tolerance)) { + DiffLine mod = DiffLineInitMod(og_idx, og_line, nw_idx, nw_line); + VecPushBack(&diff, mod); + VecAt(&og_matched, og_idx) = true; + VecAt(&nw_matched, nw_idx) = true; + break; + } + }); + } }); - // Phase 4: Handle remaining unmatched lines (removals and additions) + // Phase 3: Handle remaining unmatched lines (removals and additions) VecForeachPtrIdx(&og_lines, og_line, og_idx, { if (!VecAt(&og_matched, og_idx)) { DiffLine removed = DiffLineInitRem(og_idx, og_line); @@ -222,7 +223,7 @@ DiffLines GetDiff (Str* og, Str* nw) { } }); - // Phase 5: Sort the diff results by line number for proper output order + // Phase 4: Sort the diff results by line number for proper output order VecSort(&diff, DiffLineNumberCompare); // Cleanup From 15db12606f17e0ee8d03e251824ef437594158e3 Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Fri, 13 Jun 2025 03:39:53 +0530 Subject: [PATCH 07/10] Use Edit Distance For Fuzzy Matching --- Bin/CMakeLists.txt | 26 +++++++ Bin/ReaiDiff.c | 167 ++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 3 + Include/Reai/Diff.h | 15 ++-- Include/Reai/Util/Str.h | 4 +- Source/Reai/Diff.c | 135 +++++++++++++++++++------------- 6 files changed, 290 insertions(+), 60 deletions(-) create mode 100644 Bin/CMakeLists.txt create mode 100644 Bin/ReaiDiff.c diff --git a/Bin/CMakeLists.txt b/Bin/CMakeLists.txt new file mode 100644 index 0000000..d73020f --- /dev/null +++ b/Bin/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.5) + +# Create the ReaiDiff executable +add_executable(ReaiDiff ReaiDiff.c) + +# Link against the CREAIT library +target_link_libraries(ReaiDiff + PRIVATE + reai + ${CURL_LIBRARIES} +) + +# Include directories for headers +target_include_directories(ReaiDiff + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../Include" + "${CMAKE_PREFIX_PATH}/include" +) + +# Set output directory +set_target_properties(ReaiDiff PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" +) + +# Install the executable +install(TARGETS ReaiDiff DESTINATION bin) \ No newline at end of file diff --git a/Bin/ReaiDiff.c b/Bin/ReaiDiff.c new file mode 100644 index 0000000..ccbd6e0 --- /dev/null +++ b/Bin/ReaiDiff.c @@ -0,0 +1,167 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// ANSI color codes for terminal output +#define RESET "\033[0m" +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" +#define MAGENTA "\033[35m" +#define CYAN "\033[36m" +#define BOLD "\033[1m" + +// Function to read a file into a Str object +bool ReadFileToStr(const char* filename, Str* str) { + char* data = NULL; + size file_size = 0; + size capacity = 0; + + if (!ReadCompleteFile(filename, &data, &file_size, &capacity)) { + fprintf(stderr, "Error: Failed to read file '%s'\n", filename); + return false; + } + + // Initialize Str with the file content + *str = StrInitFromCstr(data, file_size); + // Free the original data since StrInitFromCstr makes a copy + free(data); + + return true; +} + +// Function to print a line with proper formatting +void PrintLine(const char* prefix, const char* color, u64 line_num, const Str* content) { + printf("%s%s%3llu: %.*s%s\n", color, prefix, line_num + 1, + (int)content->length, content->data, RESET); +} + +// Function to print diff results with colors +void PrintDiff(const char* file1, const char* file2, const DiffLines* diff) { + printf("%s--- %s%s\n", BOLD, file1, RESET); + printf("%s+++ %s%s\n", BOLD, file2, RESET); + + if (diff->length == 0) { + printf("%sFiles are identical%s\n", GREEN, RESET); + return; + } + + VecForeachPtr(diff, line, { + switch (line->type) { + case DIFF_TYPE_SAM: { + PrintLine(" ", "", line->sam.line, &line->sam.content); + break; + } + + case DIFF_TYPE_ADD: { + PrintLine("+", GREEN, line->add.line, &line->add.content); + break; + } + + case DIFF_TYPE_REM: { + PrintLine("-", RED, line->rem.line, &line->rem.content); + break; + } + + case DIFF_TYPE_MOD: { + PrintLine("-", RED, line->mod.old_line, &line->mod.old_content); + PrintLine("+", GREEN, line->mod.new_line, &line->mod.new_content); + break; + } + + case DIFF_TYPE_MOV: { + // Show moved lines with yellow color + printf("%s~ Line %llu moved to %llu: %.*s%s\n", + YELLOW, line->mov.old_line + 1, line->mov.new_line + 1, + (int)line->mov.new_content.length, line->mov.new_content.data, RESET); + break; + } + + default: { + fprintf(stderr, "Warning: Unknown diff type '%c'\n", line->type); + break; + } + } + }); +} + +// Function to print usage information +void PrintUsage(const char* program_name) { + printf("Usage: %s \n", program_name); + printf("\nCompare two files line by line and show differences.\n"); + printf("\nColor codes:\n"); + printf(" %s- Red:%s Lines removed from file1\n", RED, RESET); + printf(" %s+ Green:%s Lines added in file2\n", GREEN, RESET); + printf(" %s~ Yellow:%s Lines moved between files\n", YELLOW, RESET); + printf(" Normal: Lines that are the same\n"); + printf("\nModifications are shown as a removal followed by an addition.\n"); +} + +int main(int argc, char* argv[]) { + // Check command line arguments + if (argc != 3) { + PrintUsage(argv[0]); + return 1; + } + + const char* file1 = argv[1]; + const char* file2 = argv[2]; + + // Initialize strings for file contents + Str str1, str2; + bool success = true; + + // Read first file + if (!ReadFileToStr(file1, &str1)) { + return 1; + } + + // Read second file + if (!ReadFileToStr(file2, &str2)) { + StrDeinit(&str1); + return 1; + } + + // Get the diff + DiffLines diff = GetDiff(&str1, &str2); + + // Print the diff results + PrintDiff(file1, file2, &diff); + + // Count different types of changes for summary + size same_count = 0, add_count = 0, rem_count = 0, mod_count = 0, mov_count = 0; + VecForeachPtr(&diff, line, { + switch (line->type) { + case DIFF_TYPE_SAM: same_count++; break; + case DIFF_TYPE_ADD: add_count++; break; + case DIFF_TYPE_REM: rem_count++; break; + case DIFF_TYPE_MOD: mod_count++; break; + case DIFF_TYPE_MOV: mov_count++; break; + } + }); + + // Print summary + printf("\n%sSummary:%s\n", BOLD, RESET); + printf(" Same lines: %zu\n", same_count); + printf(" Added lines: %s%zu%s\n", GREEN, add_count, RESET); + printf(" Removed lines: %s%zu%s\n", RED, rem_count, RESET); + printf(" Modified lines: %s%zu%s\n", YELLOW, mod_count, RESET); + printf(" Moved lines: %s%zu%s\n", CYAN, mov_count, RESET); + printf(" Total changes: %zu\n", add_count + rem_count + mod_count + mov_count); + + // Cleanup + VecDeinit(&diff); + StrDeinit(&str1); + StrDeinit(&str2); + + // Return non-zero if files differ + return (add_count + rem_count + mod_count + mov_count) > 0 ? 1 : 0; +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 80a4e2b..5f7ea50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,9 @@ include_directories("${CMAKE_PREFIX_PATH}/include") # Add library source add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/Source/Reai") +# Add binary tools +add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/Bin") + # Add installation target for include files install(DIRECTORY Include/Reai DESTINATION include) diff --git a/Include/Reai/Diff.h b/Include/Reai/Diff.h index e2870a0..f3f8e5b 100644 --- a/Include/Reai/Diff.h +++ b/Include/Reai/Diff.h @@ -36,13 +36,13 @@ typedef Vec (DiffLine) DiffLines; (DiffLine {.type = DIFF_TYPE_ADD, .add.line = (l), .add.content = StrDup (s)}) # define DiffLineInitRem(l, s) \ (DiffLine {.type = DIFF_TYPE_REM, .rem.line = (l), .rem.content = StrDup (s)}) -# define DiffLineInitMov(lo, so, ln, sn) \ +# define DiffLineInitMov(lo, ln, s) \ (DiffLine { \ .type = DIFF_TYPE_MOV, \ .mov.old_line = (lo), \ .mov.new_line = (ln), \ - .mov.old_content = StrDup (so), \ - .mov.new_content = StrDup (sn) \ + .mov.old_content = StrDup (s), \ + .mov.new_content = StrDup (s) \ }) # define DiffLineInitMod(lo, so, ln, sn) \ (DiffLine { \ @@ -59,12 +59,12 @@ typedef Vec (DiffLine) DiffLines; ((DiffLine) {.type = DIFF_TYPE_ADD, .add.line = (l), .add.content = StrDup (s)}) # define DiffLineInitRem(l, s) \ ((DiffLine) {.type = DIFF_TYPE_REM, .rem.line = (l), .rem.content = StrDup (s)}) -# define DiffLineInitMov(lo, so, ln, sn) \ +# define DiffLineInitMov(lo, ln, s) \ ((DiffLine) {.type = DIFF_TYPE_MOV, \ .mov.old_line = (lo), \ .mov.new_line = (ln), \ - .mov.old_content = StrDup (so), \ - .mov.new_content = StrDup (sn)}) + .mov.old_content = StrDup (s), \ + .mov.new_content = StrDup (s)}) # define DiffLineInitMod(lo, so, ln, sn) \ ((DiffLine) {.type = DIFF_TYPE_MOD, \ .mov.old_line = (lo), \ @@ -80,6 +80,9 @@ extern "C" { REAI_API bool DiffLineInitClone (DiffLine* dst, DiffLine* src); REAI_API void DiffLineDeinit (DiffLine* dl); + REAI_API u32 StrLevenshteinDistance (Str* s1, Str* s2); + REAI_API bool StrAreSimilar (Str* s1, Str* s2, u32 max_distance); + REAI_API DiffLines GetDiff (Str* og, Str* nw); #ifdef __cplusplus diff --git a/Include/Reai/Util/Str.h b/Include/Reai/Util/Str.h index a4226ff..e3e2f5f 100644 --- a/Include/Reai/Util/Str.h +++ b/Include/Reai/Util/Str.h @@ -399,8 +399,8 @@ extern "C" { #define StrLast(str) VecLast (str) #define StrBegin(str) VecBegin (str) #define StrEnd(str) VecEnd (str) -#define StrCharAt(str) VecAt (str, idx) -#define StrCharPtrAt(str) VecPtrAt (str, idx) +#define StrCharAt(str, idx) VecAt (str, idx) +#define StrCharPtrAt(str, idx) VecPtrAt (str, idx) /// Push an array of chars with given length to the back of this string. /// diff --git a/Source/Reai/Diff.c b/Source/Reai/Diff.c index aafcfb8..2c1080e 100644 --- a/Source/Reai/Diff.c +++ b/Source/Reai/Diff.c @@ -67,78 +67,107 @@ void DiffLineDeinit (DiffLine* dl) { dl->type = 0; } -int DiffLineNumberCompare(const void* a, const void* b) { - const DiffLine* line_a = (const DiffLine*)a; - const DiffLine* line_b = (const DiffLine*)b; - - u64 line_a_num, line_b_num; - - // Get the primary line number for sorting (using old/source line numbers) - switch (line_a->type) { +u64 getDiffLineNumber(const DiffLine* line) { + switch (line->type) { case DIFF_TYPE_SAM: case DIFF_TYPE_REM: - line_a_num = line_a->sam.line; - break; case DIFF_TYPE_ADD: - line_a_num = line_a->add.line + 1000000; // Sort additions after source lines - break; + return line->sam.line; // All use the same union field case DIFF_TYPE_MOV: case DIFF_TYPE_MOD: - line_a_num = line_a->mov.old_line; - break; + return line->mov.old_line; default: - line_a_num = 0; + return 0; } +} + +int DiffLineNumberCompare(const void* a, const void* b) { + const DiffLine* line_a = (const DiffLine*)a; + const DiffLine* line_b = (const DiffLine*)b; - switch (line_b->type) { - case DIFF_TYPE_SAM: - case DIFF_TYPE_REM: - line_b_num = line_b->sam.line; - break; - case DIFF_TYPE_ADD: - line_b_num = line_b->add.line + 1000000; // Sort additions after source lines - break; - case DIFF_TYPE_MOV: - case DIFF_TYPE_MOD: - line_b_num = line_b->mov.old_line; - break; - default: - line_b_num = 0; - } + u64 line_a_num = getDiffLineNumber(line_a); + u64 line_b_num = getDiffLineNumber(line_b); if (line_a_num < line_b_num) return -1; if (line_a_num > line_b_num) return 1; return 0; } -bool StrFuzzyCmp (Str* s1, Str* s2, u32 error_tolerance) { +u32 StrLevenshteinDistance (Str* s1, Str* s2) { if (!s1 || !s2) { LOG_FATAL ("Invalid arguments"); } - u64 l1 = s1->length; - u64 l2 = s2->length; - const char* p1 = s1->data; - const char* p2 = s2->data; + u64 len1 = s1->length; + u64 len2 = s2->length; + const char* str1 = s1->data; + const char* str2 = s2->data; - u64 length_diff = MAX2 (l1, l2) - MIN2 (l1, l2); - if (length_diff > error_tolerance) { - return false; - } else { - error_tolerance -= length_diff; + // Handle empty strings + if (len1 == 0) return len2; + if (len2 == 0) return len1; + + // Create matrix for dynamic programming + // We only need two rows for space optimization + u32* prev_row = (u32*)malloc((len2 + 1) * sizeof(u32)); + u32* curr_row = (u32*)malloc((len2 + 1) * sizeof(u32)); + + if (!prev_row || !curr_row) { + if (prev_row) free(prev_row); + if (curr_row) free(curr_row); + LOG_FATAL("Memory allocation failed for Levenshtein distance calculation"); } - u64 l = MIN2 (l1, l2); - while (l-- && *p1 && *p2) { - if (*p1 != *p2) { - if (!error_tolerance) { - return false; - } - error_tolerance--; + // Initialize first row (distance from empty string) + for (u64 j = 0; j <= len2; j++) { + prev_row[j] = j; + } + + // Fill the matrix row by row + for (u64 i = 1; i <= len1; i++) { + curr_row[0] = i; // Distance from empty string + + for (u64 j = 1; j <= len2; j++) { + u32 cost = (str1[i-1] == str2[j-1]) ? 0 : 1; + + // Calculate minimum of three operations: + // 1. Deletion: curr_row[j-1] + 1 + // 2. Insertion: prev_row[j] + 1 + // 3. Substitution: prev_row[j-1] + cost + u32 deletion = curr_row[j-1] + 1; + u32 insertion = prev_row[j] + 1; + u32 substitution = prev_row[j-1] + cost; + + curr_row[j] = MIN2(MIN2(deletion, insertion), substitution); } + + // Swap rows for next iteration + u32* temp = prev_row; + prev_row = curr_row; + curr_row = temp; } - return true; + u32 distance = prev_row[len2]; + + free(prev_row); + free(curr_row); + + return distance; +} + +bool StrAreSimilar (Str* s1, Str* s2, u32 max_distance) { + if (!s1 || !s2) { + LOG_FATAL ("Invalid arguments"); + } + + // Quick check: if length difference is already > max_distance, they can't be similar + u64 length_diff = (s1->length > s2->length) ? (s1->length - s2->length) : (s2->length - s1->length); + if (length_diff > max_distance) { + return false; + } + + u32 distance = StrLevenshteinDistance(s1, s2); + return distance <= max_distance; } DiffLines GetDiff (Str* og, Str* nw) { @@ -180,7 +209,7 @@ DiffLines GetDiff (Str* og, Str* nw) { // First check for exact match (move) if (StrCmp(og_line, nw_line) == 0) { - DiffLine move = DiffLineInitMov(og_idx, og_line, nw_idx, nw_line); + DiffLine move = DiffLineInitMov(og_idx, nw_idx, nw_line); VecPushBack(&diff, move); VecAt(&og_matched, og_idx) = true; VecAt(&nw_matched, nw_idx) = true; @@ -194,10 +223,12 @@ DiffLines GetDiff (Str* og, Str* nw) { VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { if (VecAt(&nw_matched, nw_idx)) continue; - // Calculate fuzzy match tolerance (50% of the longer string) - u32 tolerance = MAX2(og_line->length, nw_line->length) / 2; + // Calculate max distance based on string lengths + // Use 20% of the average length, with minimum of 2 and maximum of 10 + u64 avg_length = (og_line->length + nw_line->length) / 2; + u32 max_distance = MAX2(2, MIN2(10, avg_length / 5)); - if (StrFuzzyCmp(og_line, nw_line, tolerance)) { + if (StrAreSimilar(og_line, nw_line, max_distance)) { DiffLine mod = DiffLineInitMod(og_idx, og_line, nw_idx, nw_line); VecPushBack(&diff, mod); VecAt(&og_matched, og_idx) = true; From de72c93a56903572fe5f9a87a70b514be17bbb54 Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Fri, 13 Jun 2025 13:50:34 +0530 Subject: [PATCH 08/10] Fix Request Methods --- Source/Reai/Api.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Reai/Api.c b/Source/Reai/Api.c index 665e05f..e68e729 100644 --- a/Source/Reai/Api.c +++ b/Source/Reai/Api.c @@ -1178,7 +1178,7 @@ Comments GetDecompilationComments (Connection* conn, FunctionId function_id) { Str gj = StrInit(); - if (MakeRequest (&conn->api_key, &url, NULL, &gj, "POST")) { + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "GET")) { StrDeinit (&url); StrIter j = StrIterInitFromStr (&gj); @@ -1236,13 +1236,13 @@ CommentId AddDecompilationComment ( StrPrintf (&url, "%s/v2/functions/%llu/decompilation/comments", conn->host.data, function_id); Str sj = StrInit(); - StrPrintf ( - &sj, - "{\"content\":\"%s\", \"context\"{\"start_line\":%llu, \"end_line\":%llu}}", - comment->data, - start_line, - end_line - ); + JW_OBJ (sj, { + JW_STR_KV (sj, "content", *comment); + JW_OBJ_KV (sj, "context", { + JW_INT_KV (sj, "start_line", start_line); + JW_INT_KV (sj, "end_line", end_line); + }); + }); Str gj = StrInit(); @@ -1294,7 +1294,7 @@ bool UpdateDecompilationComment ( Str gj = StrInit(); - if (MakeRequest (&conn->api_key, &url, &sj, &gj, "DELETE")) { + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "PATCH")) { StrDeinit (&url); StrDeinit (&sj); @@ -1364,7 +1364,7 @@ Comments GetAiDecompilationComments (Connection* conn, FunctionId function_id) { Str gj = StrInit(); - if (MakeRequest (&conn->api_key, &url, NULL, &gj, "POST")) { + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "GET")) { StrDeinit (&url); StrIter j = StrIterInitFromStr (&gj); @@ -1427,13 +1427,13 @@ CommentId AddAiDecompilationComment ( ); Str sj = StrInit(); - StrPrintf ( - &sj, - "{\"content\":\"%s\", \"context\"{\"start_line\":%llu, \"end_line\":%llu}}", - comment->data, - start_line, - end_line - ); + JW_OBJ (sj, { + JW_STR_KV (sj, "content", *comment); + JW_OBJ_KV (sj, "context", { + JW_INT_KV (sj, "start_line", start_line); + JW_INT_KV (sj, "end_line", end_line); + }); + }); Str gj = StrInit(); @@ -1485,7 +1485,7 @@ bool UpdateAiDecompilationComment ( Str gj = StrInit(); - if (MakeRequest (&conn->api_key, &url, &sj, &gj, "DELETE")) { + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "PATCH")) { StrDeinit (&url); StrDeinit (&sj); @@ -1550,7 +1550,7 @@ REAI_API Comments GetComments (Connection* conn) { Str gj = StrInit(); - if (MakeRequest (&conn->api_key, &url, NULL, &gj, "POST")) { + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "GET")) { StrDeinit (&url); StrIter j = StrIterInitFromStr (&gj); From 2d2b84e11f684fe3e93792573f28737a05570cc6 Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Fri, 13 Jun 2025 20:04:41 +0530 Subject: [PATCH 09/10] Diff Fixes --- Bin/ReaiDiff.c | 34 +++++++++++++++--------- Source/Reai/Api.c | 4 +-- Source/Reai/Diff.c | 66 +++++++++++++++++++++++++++++++++------------- 3 files changed, 71 insertions(+), 33 deletions(-) diff --git a/Bin/ReaiDiff.c b/Bin/ReaiDiff.c index ccbd6e0..9e02ab5 100644 --- a/Bin/ReaiDiff.c +++ b/Bin/ReaiDiff.c @@ -38,10 +38,15 @@ bool ReadFileToStr(const char* filename, Str* str) { return true; } -// Function to print a line with proper formatting -void PrintLine(const char* prefix, const char* color, u64 line_num, const Str* content) { - printf("%s%s%3llu: %.*s%s\n", color, prefix, line_num + 1, - (int)content->length, content->data, RESET); +// Function to print a line with proper formatting and type info +void PrintLine(const char* prefix, const char* color, u64 line_num, const Str* content, const char* type_info) { + if (type_info && strlen(type_info) > 0) { + printf("%s%s%3llu: %.*s%s %s[%s]%s\n", color, prefix, line_num + 1, + (int)content->length, content->data, RESET, BLUE, type_info, RESET); + } else { + printf("%s%s%3llu: %.*s%s\n", color, prefix, line_num + 1, + (int)content->length, content->data, RESET); + } } // Function to print diff results with colors @@ -57,31 +62,34 @@ void PrintDiff(const char* file1, const char* file2, const DiffLines* diff) { VecForeachPtr(diff, line, { switch (line->type) { case DIFF_TYPE_SAM: { - PrintLine(" ", "", line->sam.line, &line->sam.content); + PrintLine(" ", "", line->sam.line, &line->sam.content, ""); break; } case DIFF_TYPE_ADD: { - PrintLine("+", GREEN, line->add.line, &line->add.content); + PrintLine("+", GREEN, line->add.line, &line->add.content, "ADDED"); break; } case DIFF_TYPE_REM: { - PrintLine("-", RED, line->rem.line, &line->rem.content); + PrintLine("-", RED, line->rem.line, &line->rem.content, "REMOVED"); break; } case DIFF_TYPE_MOD: { - PrintLine("-", RED, line->mod.old_line, &line->mod.old_content); - PrintLine("+", GREEN, line->mod.new_line, &line->mod.new_content); + char mod_info[64]; + snprintf(mod_info, sizeof(mod_info), "MODIFIED: was line %llu", line->mod.old_line + 1); + PrintLine("-", RED, line->mod.old_line, &line->mod.old_content, "MODIFIED: old"); + PrintLine("+", GREEN, line->mod.new_line, &line->mod.new_content, mod_info); break; } case DIFF_TYPE_MOV: { - // Show moved lines with yellow color - printf("%s~ Line %llu moved to %llu: %.*s%s\n", - YELLOW, line->mov.old_line + 1, line->mov.new_line + 1, - (int)line->mov.new_content.length, line->mov.new_content.data, RESET); + // Show moved lines with yellow color and clear info + printf("%s~ %3llu: %.*s%s %s[MOVED: from line %llu]%s\n", + YELLOW, line->mov.new_line + 1, + (int)line->mov.new_content.length, line->mov.new_content.data, RESET, + BLUE, line->mov.old_line + 1, RESET); break; } diff --git a/Source/Reai/Api.c b/Source/Reai/Api.c index e68e729..2b93f4d 100644 --- a/Source/Reai/Api.c +++ b/Source/Reai/Api.c @@ -1226,7 +1226,7 @@ CommentId AddDecompilationComment ( u64 start_line, u64 end_line ) { - if (!conn || !function_id || !comment || start_line >= end_line) { + if (!conn || !function_id || !comment || start_line > end_line) { LOG_ERROR ("Invalid arguments"); return 0; } @@ -1412,7 +1412,7 @@ CommentId AddAiDecompilationComment ( u64 start_line, u64 end_line ) { - if (!conn || !function_id || !comment || start_line >= end_line) { + if (!conn || !function_id || !comment || start_line > end_line) { LOG_ERROR ("Invalid arguments"); return 0; } diff --git a/Source/Reai/Diff.c b/Source/Reai/Diff.c index 2c1080e..ea3f052 100644 --- a/Source/Reai/Diff.c +++ b/Source/Reai/Diff.c @@ -67,7 +67,7 @@ void DiffLineDeinit (DiffLine* dl) { dl->type = 0; } -u64 getDiffLineNumber(const DiffLine* line) { +u64 getDiffOldLineNumber(const DiffLine* line) { switch (line->type) { case DIFF_TYPE_SAM: case DIFF_TYPE_REM: @@ -77,20 +77,27 @@ u64 getDiffLineNumber(const DiffLine* line) { case DIFF_TYPE_MOD: return line->mov.old_line; default: - return 0; + LOG_FATAL("Invalid diff line type: %c", line->type); } } -int DiffLineNumberCompare(const void* a, const void* b) { - const DiffLine* line_a = (const DiffLine*)a; - const DiffLine* line_b = (const DiffLine*)b; - - u64 line_a_num = getDiffLineNumber(line_a); - u64 line_b_num = getDiffLineNumber(line_b); - - if (line_a_num < line_b_num) return -1; - if (line_a_num > line_b_num) return 1; - return 0; +u64 getDiffNewLineNumber(const DiffLine* line) { + switch (line->type) { + case DIFF_TYPE_SAM: + case DIFF_TYPE_REM: + case DIFF_TYPE_ADD: + return line->sam.line; // All use the same union field + case DIFF_TYPE_MOV: + case DIFF_TYPE_MOD: + return line->mov.new_line; + default: + LOG_FATAL("Invalid diff line type: %c", line->type); + } +} + +int DiffLineNumberCompare(const DiffLine* a, const DiffLine* b) { + if (getDiffNewLineNumber(a) <= getDiffNewLineNumber(b)) return -1; + else return 1; } u32 StrLevenshteinDistance (Str* s1, Str* s2) { @@ -190,9 +197,18 @@ DiffLines GetDiff (Str* og, Str* nw) { // Phase 1: Find exact matches in the same positions size_t min_length = og_lines.length < nw_lines.length ? og_lines.length : nw_lines.length; - for (size_t i = 0; i < min_length; i++) { - if (StrCmp(VecPtrAt(&og_lines, i), VecPtrAt(&nw_lines, i)) == 0) { - DiffLine same = DiffLineInitSam(i, VecPtrAt(&og_lines, i)); + + for (size_t i = 0; i < min_length; i++) { + Str* og_line = VecPtrAt(&og_lines, i); + Str* nw_line = VecPtrAt(&nw_lines, i); + + // Skip empty lines in Phase 1 - they're not meaningful for exact matching + if (og_line->length == 0 || nw_line->length == 0) { + continue; + } + + if (StrCmp(og_line, nw_line) == 0) { + DiffLine same = DiffLineInitSam(i, og_line); VecPushBack(&diff, same); VecAt(&og_matched, i) = true; VecAt(&nw_matched, i) = true; @@ -201,12 +217,22 @@ DiffLines GetDiff (Str* og, Str* nw) { // Phase 2: Find moves and modifications in a single pass VecForeachPtrIdx(&og_lines, og_line, og_idx, { - if (VecAt(&og_matched, og_idx)) continue; + if (VecAt(&og_matched, og_idx)) { + continue; + } + + // Skip empty lines in Phase 2 - they're not meaningful for moves/modifications + if (og_line->length == 0) { + continue; + } bool found_match = false; VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { if (VecAt(&nw_matched, nw_idx)) continue; + // Skip empty lines when looking for matches + if (nw_line->length == 0) continue; + // First check for exact match (move) if (StrCmp(og_line, nw_line) == 0) { DiffLine move = DiffLineInitMov(og_idx, nw_idx, nw_line); @@ -223,10 +249,14 @@ DiffLines GetDiff (Str* og, Str* nw) { VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { if (VecAt(&nw_matched, nw_idx)) continue; + // Skip empty lines when looking for fuzzy matches + if (nw_line->length == 0) continue; + // Calculate max distance based on string lengths - // Use 20% of the average length, with minimum of 2 and maximum of 10 + // Use 25% of the average length, with minimum of 3 and maximum of 15 + // Be more lenient to catch indentation changes u64 avg_length = (og_line->length + nw_line->length) / 2; - u32 max_distance = MAX2(2, MIN2(10, avg_length / 5)); + u32 max_distance = MAX2(3, MIN2(15, avg_length / 4)); if (StrAreSimilar(og_line, nw_line, max_distance)) { DiffLine mod = DiffLineInitMod(og_idx, og_line, nw_idx, nw_line); From e7be00f779d5ef8af5da12d782c6fd7c61aba5dd Mon Sep 17 00:00:00 2001 From: Siddharth Mishra Date: Fri, 13 Jun 2025 20:05:08 +0530 Subject: [PATCH 10/10] Code Formatting --- Bin/ReaiDiff.c | 236 ++++++++++++++++++++++++---------------- Include/Reai/Diff.h | 2 +- Include/Reai/Util/Str.h | 8 +- Source/Reai/Diff.c | 223 +++++++++++++++++++------------------ 4 files changed, 262 insertions(+), 207 deletions(-) diff --git a/Bin/ReaiDiff.c b/Bin/ReaiDiff.c index 9e02ab5..5517088 100644 --- a/Bin/ReaiDiff.c +++ b/Bin/ReaiDiff.c @@ -1,13 +1,12 @@ -#include -#include -#include - -#include -#include #include +#include #include +#include #include #include +#include +#include +#include // ANSI color codes for terminal output #define RESET "\033[0m" @@ -20,81 +19,116 @@ #define BOLD "\033[1m" // Function to read a file into a Str object -bool ReadFileToStr(const char* filename, Str* str) { - char* data = NULL; - size file_size = 0; - size capacity = 0; - - if (!ReadCompleteFile(filename, &data, &file_size, &capacity)) { - fprintf(stderr, "Error: Failed to read file '%s'\n", filename); +bool ReadFileToStr (const char* filename, Str* str) { + char* data = NULL; + size file_size = 0; + size capacity = 0; + + if (!ReadCompleteFile (filename, &data, &file_size, &capacity)) { + fprintf (stderr, "Error: Failed to read file '%s'\n", filename); return false; } - + // Initialize Str with the file content - *str = StrInitFromCstr(data, file_size); + *str = StrInitFromCstr (data, file_size); // Free the original data since StrInitFromCstr makes a copy - free(data); - + free (data); + return true; } // Function to print a line with proper formatting and type info -void PrintLine(const char* prefix, const char* color, u64 line_num, const Str* content, const char* type_info) { - if (type_info && strlen(type_info) > 0) { - printf("%s%s%3llu: %.*s%s %s[%s]%s\n", color, prefix, line_num + 1, - (int)content->length, content->data, RESET, BLUE, type_info, RESET); +void PrintLine ( + const char* prefix, + const char* color, + u64 line_num, + const Str* content, + const char* type_info +) { + if (type_info && strlen (type_info) > 0) { + printf ( + "%s%s%3llu: %.*s%s %s[%s]%s\n", + color, + prefix, + line_num + 1, + (int)content->length, + content->data, + RESET, + BLUE, + type_info, + RESET + ); } else { - printf("%s%s%3llu: %.*s%s\n", color, prefix, line_num + 1, - (int)content->length, content->data, RESET); + printf ( + "%s%s%3llu: %.*s%s\n", + color, + prefix, + line_num + 1, + (int)content->length, + content->data, + RESET + ); } } // Function to print diff results with colors -void PrintDiff(const char* file1, const char* file2, const DiffLines* diff) { - printf("%s--- %s%s\n", BOLD, file1, RESET); - printf("%s+++ %s%s\n", BOLD, file2, RESET); - +void PrintDiff (const char* file1, const char* file2, const DiffLines* diff) { + printf ("%s--- %s%s\n", BOLD, file1, RESET); + printf ("%s+++ %s%s\n", BOLD, file2, RESET); + if (diff->length == 0) { - printf("%sFiles are identical%s\n", GREEN, RESET); + printf ("%sFiles are identical%s\n", GREEN, RESET); return; } - - VecForeachPtr(diff, line, { + + VecForeachPtr (diff, line, { switch (line->type) { - case DIFF_TYPE_SAM: { - PrintLine(" ", "", line->sam.line, &line->sam.content, ""); + case DIFF_TYPE_SAM : { + PrintLine (" ", "", line->sam.line, &line->sam.content, ""); break; } - - case DIFF_TYPE_ADD: { - PrintLine("+", GREEN, line->add.line, &line->add.content, "ADDED"); + + case DIFF_TYPE_ADD : { + PrintLine ("+", GREEN, line->add.line, &line->add.content, "ADDED"); break; } - - case DIFF_TYPE_REM: { - PrintLine("-", RED, line->rem.line, &line->rem.content, "REMOVED"); + + case DIFF_TYPE_REM : { + PrintLine ("-", RED, line->rem.line, &line->rem.content, "REMOVED"); break; } - - case DIFF_TYPE_MOD: { + + case DIFF_TYPE_MOD : { char mod_info[64]; - snprintf(mod_info, sizeof(mod_info), "MODIFIED: was line %llu", line->mod.old_line + 1); - PrintLine("-", RED, line->mod.old_line, &line->mod.old_content, "MODIFIED: old"); - PrintLine("+", GREEN, line->mod.new_line, &line->mod.new_content, mod_info); + snprintf ( + mod_info, + sizeof (mod_info), + "MODIFIED: was line %llu", + line->mod.old_line + 1 + ); + PrintLine ("-", RED, line->mod.old_line, &line->mod.old_content, "MODIFIED: old"); + PrintLine ("+", GREEN, line->mod.new_line, &line->mod.new_content, mod_info); break; } - - case DIFF_TYPE_MOV: { + + case DIFF_TYPE_MOV : { // Show moved lines with yellow color and clear info - printf("%s~ %3llu: %.*s%s %s[MOVED: from line %llu]%s\n", - YELLOW, line->mov.new_line + 1, - (int)line->mov.new_content.length, line->mov.new_content.data, RESET, - BLUE, line->mov.old_line + 1, RESET); + printf ( + "%s~ %3llu: %.*s%s %s[MOVED: from line %llu]%s\n", + YELLOW, + line->mov.new_line + 1, + (int)line->mov.new_content.length, + line->mov.new_content.data, + RESET, + BLUE, + line->mov.old_line + 1, + RESET + ); break; } - - default: { - fprintf(stderr, "Warning: Unknown diff type '%c'\n", line->type); + + default : { + fprintf (stderr, "Warning: Unknown diff type '%c'\n", line->type); break; } } @@ -102,74 +136,84 @@ void PrintDiff(const char* file1, const char* file2, const DiffLines* diff) { } // Function to print usage information -void PrintUsage(const char* program_name) { - printf("Usage: %s \n", program_name); - printf("\nCompare two files line by line and show differences.\n"); - printf("\nColor codes:\n"); - printf(" %s- Red:%s Lines removed from file1\n", RED, RESET); - printf(" %s+ Green:%s Lines added in file2\n", GREEN, RESET); - printf(" %s~ Yellow:%s Lines moved between files\n", YELLOW, RESET); - printf(" Normal: Lines that are the same\n"); - printf("\nModifications are shown as a removal followed by an addition.\n"); +void PrintUsage (const char* program_name) { + printf ("Usage: %s \n", program_name); + printf ("\nCompare two files line by line and show differences.\n"); + printf ("\nColor codes:\n"); + printf (" %s- Red:%s Lines removed from file1\n", RED, RESET); + printf (" %s+ Green:%s Lines added in file2\n", GREEN, RESET); + printf (" %s~ Yellow:%s Lines moved between files\n", YELLOW, RESET); + printf (" Normal: Lines that are the same\n"); + printf ("\nModifications are shown as a removal followed by an addition.\n"); } -int main(int argc, char* argv[]) { +int main (int argc, char* argv[]) { // Check command line arguments if (argc != 3) { - PrintUsage(argv[0]); + PrintUsage (argv[0]); return 1; } - + const char* file1 = argv[1]; const char* file2 = argv[2]; - + // Initialize strings for file contents - Str str1, str2; + Str str1, str2; bool success = true; - + // Read first file - if (!ReadFileToStr(file1, &str1)) { + if (!ReadFileToStr (file1, &str1)) { return 1; } - + // Read second file - if (!ReadFileToStr(file2, &str2)) { - StrDeinit(&str1); + if (!ReadFileToStr (file2, &str2)) { + StrDeinit (&str1); return 1; } - + // Get the diff - DiffLines diff = GetDiff(&str1, &str2); - + DiffLines diff = GetDiff (&str1, &str2); + // Print the diff results - PrintDiff(file1, file2, &diff); - + PrintDiff (file1, file2, &diff); + // Count different types of changes for summary size same_count = 0, add_count = 0, rem_count = 0, mod_count = 0, mov_count = 0; - VecForeachPtr(&diff, line, { + VecForeachPtr (&diff, line, { switch (line->type) { - case DIFF_TYPE_SAM: same_count++; break; - case DIFF_TYPE_ADD: add_count++; break; - case DIFF_TYPE_REM: rem_count++; break; - case DIFF_TYPE_MOD: mod_count++; break; - case DIFF_TYPE_MOV: mov_count++; break; + case DIFF_TYPE_SAM : + same_count++; + break; + case DIFF_TYPE_ADD : + add_count++; + break; + case DIFF_TYPE_REM : + rem_count++; + break; + case DIFF_TYPE_MOD : + mod_count++; + break; + case DIFF_TYPE_MOV : + mov_count++; + break; } }); - + // Print summary - printf("\n%sSummary:%s\n", BOLD, RESET); - printf(" Same lines: %zu\n", same_count); - printf(" Added lines: %s%zu%s\n", GREEN, add_count, RESET); - printf(" Removed lines: %s%zu%s\n", RED, rem_count, RESET); - printf(" Modified lines: %s%zu%s\n", YELLOW, mod_count, RESET); - printf(" Moved lines: %s%zu%s\n", CYAN, mov_count, RESET); - printf(" Total changes: %zu\n", add_count + rem_count + mod_count + mov_count); - + printf ("\n%sSummary:%s\n", BOLD, RESET); + printf (" Same lines: %zu\n", same_count); + printf (" Added lines: %s%zu%s\n", GREEN, add_count, RESET); + printf (" Removed lines: %s%zu%s\n", RED, rem_count, RESET); + printf (" Modified lines: %s%zu%s\n", YELLOW, mod_count, RESET); + printf (" Moved lines: %s%zu%s\n", CYAN, mov_count, RESET); + printf (" Total changes: %zu\n", add_count + rem_count + mod_count + mov_count); + // Cleanup - VecDeinit(&diff); - StrDeinit(&str1); - StrDeinit(&str2); - + VecDeinit (&diff); + StrDeinit (&str1); + StrDeinit (&str2); + // Return non-zero if files differ return (add_count + rem_count + mod_count + mov_count) > 0 ? 1 : 0; -} \ No newline at end of file +} \ No newline at end of file diff --git a/Include/Reai/Diff.h b/Include/Reai/Diff.h index f3f8e5b..04b8b92 100644 --- a/Include/Reai/Diff.h +++ b/Include/Reai/Diff.h @@ -80,7 +80,7 @@ extern "C" { REAI_API bool DiffLineInitClone (DiffLine* dst, DiffLine* src); REAI_API void DiffLineDeinit (DiffLine* dl); - REAI_API u32 StrLevenshteinDistance (Str* s1, Str* s2); + REAI_API u32 StrLevenshteinDistance (Str* s1, Str* s2); REAI_API bool StrAreSimilar (Str* s1, Str* s2, u32 max_distance); REAI_API DiffLines GetDiff (Str* og, Str* nw); diff --git a/Include/Reai/Util/Str.h b/Include/Reai/Util/Str.h index e3e2f5f..5976b78 100644 --- a/Include/Reai/Util/Str.h +++ b/Include/Reai/Util/Str.h @@ -395,10 +395,10 @@ extern "C" { /// #define StrClear(str) VecClear (str) -#define StrFirst(str) VecFirst (str) -#define StrLast(str) VecLast (str) -#define StrBegin(str) VecBegin (str) -#define StrEnd(str) VecEnd (str) +#define StrFirst(str) VecFirst (str) +#define StrLast(str) VecLast (str) +#define StrBegin(str) VecBegin (str) +#define StrEnd(str) VecEnd (str) #define StrCharAt(str, idx) VecAt (str, idx) #define StrCharPtrAt(str, idx) VecPtrAt (str, idx) diff --git a/Source/Reai/Diff.c b/Source/Reai/Diff.c index ea3f052..cf54426 100644 --- a/Source/Reai/Diff.c +++ b/Source/Reai/Diff.c @@ -67,37 +67,39 @@ void DiffLineDeinit (DiffLine* dl) { dl->type = 0; } -u64 getDiffOldLineNumber(const DiffLine* line) { +u64 getDiffOldLineNumber (const DiffLine* line) { switch (line->type) { - case DIFF_TYPE_SAM: - case DIFF_TYPE_REM: - case DIFF_TYPE_ADD: + case DIFF_TYPE_SAM : + case DIFF_TYPE_REM : + case DIFF_TYPE_ADD : return line->sam.line; // All use the same union field - case DIFF_TYPE_MOV: - case DIFF_TYPE_MOD: + case DIFF_TYPE_MOV : + case DIFF_TYPE_MOD : return line->mov.old_line; - default: - LOG_FATAL("Invalid diff line type: %c", line->type); + default : + LOG_FATAL ("Invalid diff line type: %c", line->type); } } -u64 getDiffNewLineNumber(const DiffLine* line) { +u64 getDiffNewLineNumber (const DiffLine* line) { switch (line->type) { - case DIFF_TYPE_SAM: - case DIFF_TYPE_REM: - case DIFF_TYPE_ADD: + case DIFF_TYPE_SAM : + case DIFF_TYPE_REM : + case DIFF_TYPE_ADD : return line->sam.line; // All use the same union field - case DIFF_TYPE_MOV: - case DIFF_TYPE_MOD: + case DIFF_TYPE_MOV : + case DIFF_TYPE_MOD : return line->mov.new_line; - default: - LOG_FATAL("Invalid diff line type: %c", line->type); + default : + LOG_FATAL ("Invalid diff line type: %c", line->type); } } -int DiffLineNumberCompare(const DiffLine* a, const DiffLine* b) { - if (getDiffNewLineNumber(a) <= getDiffNewLineNumber(b)) return -1; - else return 1; +int DiffLineNumberCompare (const DiffLine* a, const DiffLine* b) { + if (getDiffNewLineNumber (a) <= getDiffNewLineNumber (b)) + return -1; + else + return 1; } u32 StrLevenshteinDistance (Str* s1, Str* s2) { @@ -105,24 +107,28 @@ u32 StrLevenshteinDistance (Str* s1, Str* s2) { LOG_FATAL ("Invalid arguments"); } - u64 len1 = s1->length; - u64 len2 = s2->length; + u64 len1 = s1->length; + u64 len2 = s2->length; const char* str1 = s1->data; const char* str2 = s2->data; // Handle empty strings - if (len1 == 0) return len2; - if (len2 == 0) return len1; + if (len1 == 0) + return len2; + if (len2 == 0) + return len1; // Create matrix for dynamic programming // We only need two rows for space optimization - u32* prev_row = (u32*)malloc((len2 + 1) * sizeof(u32)); - u32* curr_row = (u32*)malloc((len2 + 1) * sizeof(u32)); - + u32* prev_row = (u32*)malloc ((len2 + 1) * sizeof (u32)); + u32* curr_row = (u32*)malloc ((len2 + 1) * sizeof (u32)); + if (!prev_row || !curr_row) { - if (prev_row) free(prev_row); - if (curr_row) free(curr_row); - LOG_FATAL("Memory allocation failed for Levenshtein distance calculation"); + if (prev_row) + free (prev_row); + if (curr_row) + free (curr_row); + LOG_FATAL ("Memory allocation failed for Levenshtein distance calculation"); } // Initialize first row (distance from empty string) @@ -133,32 +139,32 @@ u32 StrLevenshteinDistance (Str* s1, Str* s2) { // Fill the matrix row by row for (u64 i = 1; i <= len1; i++) { curr_row[0] = i; // Distance from empty string - + for (u64 j = 1; j <= len2; j++) { - u32 cost = (str1[i-1] == str2[j-1]) ? 0 : 1; - + u32 cost = (str1[i - 1] == str2[j - 1]) ? 0 : 1; + // Calculate minimum of three operations: // 1. Deletion: curr_row[j-1] + 1 - // 2. Insertion: prev_row[j] + 1 + // 2. Insertion: prev_row[j] + 1 // 3. Substitution: prev_row[j-1] + cost - u32 deletion = curr_row[j-1] + 1; - u32 insertion = prev_row[j] + 1; - u32 substitution = prev_row[j-1] + cost; - - curr_row[j] = MIN2(MIN2(deletion, insertion), substitution); + u32 deletion = curr_row[j - 1] + 1; + u32 insertion = prev_row[j] + 1; + u32 substitution = prev_row[j - 1] + cost; + + curr_row[j] = MIN2 (MIN2 (deletion, insertion), substitution); } - + // Swap rows for next iteration u32* temp = prev_row; - prev_row = curr_row; - curr_row = temp; + prev_row = curr_row; + curr_row = temp; } u32 distance = prev_row[len2]; - - free(prev_row); - free(curr_row); - + + free (prev_row); + free (curr_row); + return distance; } @@ -168,12 +174,13 @@ bool StrAreSimilar (Str* s1, Str* s2, u32 max_distance) { } // Quick check: if length difference is already > max_distance, they can't be similar - u64 length_diff = (s1->length > s2->length) ? (s1->length - s2->length) : (s2->length - s1->length); + u64 length_diff = + (s1->length > s2->length) ? (s1->length - s2->length) : (s2->length - s1->length); if (length_diff > max_distance) { return false; } - u32 distance = StrLevenshteinDistance(s1, s2); + u32 distance = StrLevenshteinDistance (s1, s2); return distance <= max_distance; } @@ -186,83 +193,87 @@ DiffLines GetDiff (Str* og, Str* nw) { Strs og_lines = StrSplit (og, "\n"); Strs nw_lines = StrSplit (nw, "\n"); - + // Create tracking vectors to mark which lines have been matched - Vec(bool) og_matched = VecInit(); - Vec(bool) nw_matched = VecInit(); - + Vec (bool) og_matched = VecInit(); + Vec (bool) nw_matched = VecInit(); + // Resize vectors to match the number of lines and initialize to false - VecResize(&og_matched, og_lines.length); - VecResize(&nw_matched, nw_lines.length); + VecResize (&og_matched, og_lines.length); + VecResize (&nw_matched, nw_lines.length); // Phase 1: Find exact matches in the same positions size_t min_length = og_lines.length < nw_lines.length ? og_lines.length : nw_lines.length; - + for (size_t i = 0; i < min_length; i++) { - Str* og_line = VecPtrAt(&og_lines, i); - Str* nw_line = VecPtrAt(&nw_lines, i); - + Str* og_line = VecPtrAt (&og_lines, i); + Str* nw_line = VecPtrAt (&nw_lines, i); + // Skip empty lines in Phase 1 - they're not meaningful for exact matching if (og_line->length == 0 || nw_line->length == 0) { continue; } - - if (StrCmp(og_line, nw_line) == 0) { - DiffLine same = DiffLineInitSam(i, og_line); - VecPushBack(&diff, same); - VecAt(&og_matched, i) = true; - VecAt(&nw_matched, i) = true; + + if (StrCmp (og_line, nw_line) == 0) { + DiffLine same = DiffLineInitSam (i, og_line); + VecPushBack (&diff, same); + VecAt (&og_matched, i) = true; + VecAt (&nw_matched, i) = true; } } // Phase 2: Find moves and modifications in a single pass - VecForeachPtrIdx(&og_lines, og_line, og_idx, { - if (VecAt(&og_matched, og_idx)) { + VecForeachPtrIdx (&og_lines, og_line, og_idx, { + if (VecAt (&og_matched, og_idx)) { continue; } - + // Skip empty lines in Phase 2 - they're not meaningful for moves/modifications if (og_line->length == 0) { continue; } - + bool found_match = false; - VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { - if (VecAt(&nw_matched, nw_idx)) continue; - + VecForeachPtrIdx (&nw_lines, nw_line, nw_idx, { + if (VecAt (&nw_matched, nw_idx)) + continue; + // Skip empty lines when looking for matches - if (nw_line->length == 0) continue; - + if (nw_line->length == 0) + continue; + // First check for exact match (move) - if (StrCmp(og_line, nw_line) == 0) { - DiffLine move = DiffLineInitMov(og_idx, nw_idx, nw_line); - VecPushBack(&diff, move); - VecAt(&og_matched, og_idx) = true; - VecAt(&nw_matched, nw_idx) = true; - found_match = true; + if (StrCmp (og_line, nw_line) == 0) { + DiffLine move = DiffLineInitMov (og_idx, nw_idx, nw_line); + VecPushBack (&diff, move); + VecAt (&og_matched, og_idx) = true; + VecAt (&nw_matched, nw_idx) = true; + found_match = true; break; } }); - + // If no exact match found, look for fuzzy match (modification) if (!found_match) { - VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { - if (VecAt(&nw_matched, nw_idx)) continue; - + VecForeachPtrIdx (&nw_lines, nw_line, nw_idx, { + if (VecAt (&nw_matched, nw_idx)) + continue; + // Skip empty lines when looking for fuzzy matches - if (nw_line->length == 0) continue; - + if (nw_line->length == 0) + continue; + // Calculate max distance based on string lengths // Use 25% of the average length, with minimum of 3 and maximum of 15 // Be more lenient to catch indentation changes - u64 avg_length = (og_line->length + nw_line->length) / 2; - u32 max_distance = MAX2(3, MIN2(15, avg_length / 4)); - - if (StrAreSimilar(og_line, nw_line, max_distance)) { - DiffLine mod = DiffLineInitMod(og_idx, og_line, nw_idx, nw_line); - VecPushBack(&diff, mod); - VecAt(&og_matched, og_idx) = true; - VecAt(&nw_matched, nw_idx) = true; + u64 avg_length = (og_line->length + nw_line->length) / 2; + u32 max_distance = MAX2 (3, MIN2 (15, avg_length / 4)); + + if (StrAreSimilar (og_line, nw_line, max_distance)) { + DiffLine mod = DiffLineInitMod (og_idx, og_line, nw_idx, nw_line); + VecPushBack (&diff, mod); + VecAt (&og_matched, og_idx) = true; + VecAt (&nw_matched, nw_idx) = true; break; } }); @@ -270,28 +281,28 @@ DiffLines GetDiff (Str* og, Str* nw) { }); // Phase 3: Handle remaining unmatched lines (removals and additions) - VecForeachPtrIdx(&og_lines, og_line, og_idx, { - if (!VecAt(&og_matched, og_idx)) { - DiffLine removed = DiffLineInitRem(og_idx, og_line); - VecPushBack(&diff, removed); + VecForeachPtrIdx (&og_lines, og_line, og_idx, { + if (!VecAt (&og_matched, og_idx)) { + DiffLine removed = DiffLineInitRem (og_idx, og_line); + VecPushBack (&diff, removed); } }); - - VecForeachPtrIdx(&nw_lines, nw_line, nw_idx, { - if (!VecAt(&nw_matched, nw_idx)) { - DiffLine added = DiffLineInitAdd(nw_idx, nw_line); - VecPushBack(&diff, added); + + VecForeachPtrIdx (&nw_lines, nw_line, nw_idx, { + if (!VecAt (&nw_matched, nw_idx)) { + DiffLine added = DiffLineInitAdd (nw_idx, nw_line); + VecPushBack (&diff, added); } }); // Phase 4: Sort the diff results by line number for proper output order - VecSort(&diff, DiffLineNumberCompare); + VecSort (&diff, DiffLineNumberCompare); // Cleanup - VecDeinit(&og_matched); - VecDeinit(&nw_matched); - VecDeinit(&og_lines); - VecDeinit(&nw_lines); + VecDeinit (&og_matched); + VecDeinit (&nw_matched); + VecDeinit (&og_lines); + VecDeinit (&nw_lines); return diff; }