Move SSD list limits to typed Kotlin

ober

d88adf779f5f41fabb959b4fb5abe3f7d4b9e95f

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index c6c89c8..98d310f 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -937,7 +937,7 @@
 
     (typed-kotlin-file "com/sfb/ssdreview/SsdDetectorGroups.kt"
       (typed-library (com sfb ssdreview)
-        (export initialGroups)
+        (export initialGroups initialGroupsLimited)
         (type Int32)
         (type BooleanArray)
         (def (detectorGroupId (index : Int32)) : String
@@ -1039,7 +1039,19 @@
                         cells
                         (growInitialGroupComponent cells seen i))
                       ignored)))
-              groups)))))
+              groups)))
+        (def (initialGroupsLimited (cells : (MutableList SsdCell))
+                                   (limit : Int32)) : (MutableList SsdGroup)
+          (let ((groups (initialGroups cells))
+                (out (mutable-list-empty SsdGroup)))
+            (let ((end (if (< (list-size groups) limit) (list-size groups) limit)))
+              (begin
+                (for/fold ((ignored (int32 0)))
+                          ((i (in-range (int32 0) end)))
+                  (begin
+                    (mutable-list-add! out (list-ref groups i))
+                    ignored))
+                out))))))
 
     (typed-kotlin-file "com/sfb/ssdreview/SourceKey.kt"
       (typed-library (com sfb ssdreview)
@@ -4602,7 +4614,8 @@
       (typed-library (com sfb ssdreview)
         (export clientLogPayloadJson clientLogCrashFields
                 clientLogFallbackPayload appLogPackageInfo appLogVersionInfo
-                deviceLogInfoJson throwableLogJson statusLogFields)
+                deviceLogInfoJson throwableLogJson statusLogFields
+                truncateClientLogStack)
         (type JSONObject)
         (type Int)
         (type Int32)
@@ -4684,6 +4697,10 @@
               (json-object-put-string! out "message" message)
               (json-object-put-string! out "stack" stack)
               out)))
+        (def (truncateClientLogStack (stack : String)) : String
+          (if (<= (string-length stack) 12000)
+            stack
+            (string-append (string-take stack (int32 12000)) "...[truncated]")))
         (def (statusLogFields (text : String)) : JSONObject
           (let ((out (json-object-empty)))
             (begin
@@ -5850,7 +5867,7 @@
        "                        detectorSource = detectorSourceWithTruth(forceDetect, existingTruthGroupCount, \"forced-android-plus-truth\", \"android-fallback\")"
        "                    }"
        "                    ssdSessionAddDetectedCellsInsideArea(session, detectedCells)"
-       "                    session.groups.addAll(initialGroups(session.cells).take(120))"
+       "                    session.groups.addAll(initialGroupsLimited(session.cells, 120))"
        "                    if (existingTruth != null && existingTruthGroupCount > 0) {"
        "                        truthStore.applyTruth(session)"
        "                        ssdSessionPruneGroupsOutsideSsdArea(session)"
@@ -6751,7 +6768,7 @@
        "        val writer = StringWriter()"
        "        error.printStackTrace(PrintWriter(writer))"
        "        val stack = writer.toString()"
-       "        val stackText = if (stack.length <= CLIENT_LOG_STACK_CHARS) stack else stack.take(CLIENT_LOG_STACK_CHARS) + \"...[truncated]\""
+       "        val stackText = truncateClientLogStack(stack)"
        "        return throwableLogJson(error.javaClass.name, error.message ?: \"\", stackText)"
        "    }"
        ""