From 07bb522fba217d9511efacd2038ab24523459677 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Sat, 10 Jan 2026 00:16:19 +0000 Subject: [PATCH 1/4] fix: significantly reduces memory usage Avoid compiling RE repeatedly in loops, especially in large metadata scripts. --- xcode/Ext-Safari/Functions.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xcode/Ext-Safari/Functions.swift b/xcode/Ext-Safari/Functions.swift index ef3c887a..144abde1 100644 --- a/xcode/Ext-Safari/Functions.swift +++ b/xcode/Ext-Safari/Functions.swift @@ -158,18 +158,18 @@ func parse(_ content: String) -> [String: Any]? { var metadata = [:] as [String: [String]] // iterate through the possible metadata keys in file if let metas = Range(match.range(at: g2), in: content) { + let p = #"^(?:[ \t]*(?:\/\/)?[ \t]*@)([\w-]+)[ \t]+([^\s]+[^\r\n\t\v\f]*)"# + // this pattern checks for specific keys that won't have values + let p2 = #"^(?:[ \t]*(?:\/\/)?[ \t]*@)(noframes)[ \t]*$"# + // force try b/c pattern is known to be valid regex + let re = try! NSRegularExpression(pattern: p, options: []) + let re2 = try! NSRegularExpression(pattern: p2, options: []) // split metadatas by new line let metaArray = content[metas].split(whereSeparator: \.isNewline) // loop through metadata lines and populate metadata dictionary for meta in metaArray { - let p = #"^(?:[ \t]*(?:\/\/)?[ \t]*@)([\w-]+)[ \t]+([^\s]+[^\r\n\t\v\f]*)"# - // this pattern checks for specific keys that won't have values - let p2 = #"^(?:[ \t]*(?:\/\/)?[ \t]*@)(noframes)[ \t]*$"# // the individual meta string, ie. // @name File Name let metaString = String(meta).trimmingCharacters(in: .whitespaces) - // force try b/c pattern is known to be valid regex - let re = try! NSRegularExpression(pattern: p, options: []) - let re2 = try! NSRegularExpression(pattern: p2, options: []) let range = NSRange(location: 0, length: metaString.utf16.count) // key lines not properly prefixed & without values will be skipped if let m = re.firstMatch(in: metaString, options: [], range: range) { From dde98697858b38649faf21959eed720a84d69123 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Sat, 10 Jan 2026 00:36:26 +0000 Subject: [PATCH 2/4] build(release): v4.8.5, v1.8.5 --- xcode/xcconfig/Userscripts-Release.xcconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xcode/xcconfig/Userscripts-Release.xcconfig b/xcode/xcconfig/Userscripts-Release.xcconfig index cd823d30..9555c1cc 100644 --- a/xcode/xcconfig/Userscripts-Release.xcconfig +++ b/xcode/xcconfig/Userscripts-Release.xcconfig @@ -1,8 +1,8 @@ #include "Userscripts-Base.xcconfig" -INFOPLIST_KEY_NSHumanReadableCopyright = Copyright © 2018–2025 Justin Wasack. All rights reserved. -IOS_APP_VERSION = 1.8.4 -MAC_APP_VERSION = 4.8.4 +INFOPLIST_KEY_NSHumanReadableCopyright = Copyright © 2018–2026 Justin Wasack. All rights reserved. +IOS_APP_VERSION = 1.8.5 +MAC_APP_VERSION = 4.8.5 // Distribution CODE_SIGN_STYLE = Manual From a23bfa5d733eafe327342e9680f19612dcf9ccd7 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Sat, 10 Jan 2026 01:45:50 +0000 Subject: [PATCH 3/4] fix: further reduces memory usage --- xcode/Ext-Safari/Functions.swift | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/xcode/Ext-Safari/Functions.swift b/xcode/Ext-Safari/Functions.swift index 144abde1..1df950d9 100644 --- a/xcode/Ext-Safari/Functions.swift +++ b/xcode/Ext-Safari/Functions.swift @@ -118,16 +118,20 @@ func isEncoded(_ str: String) -> Bool { return false } +// parser REs - force try b/c pattern is known to be valid regex +let re0 = try! NSRegularExpression(pattern: #"(?:(\/\/ ==UserScript==[ \t]*?\r?\n([\S\s]*?)\r?\n\/\/ ==\/UserScript==)([\S\s]*)|(\/\* ==UserStyle==[ \t]*?\r?\n([\S\s]*?)\r?\n==\/UserStyle== \*\/)([\S\s]*))"#, options: []) +// RE for meta line +let re1 = try! NSRegularExpression(pattern: #"^(?:[ \t]*(?:\/\/)?[ \t]*@)([\w-]+)[ \t]+([^\s]+[^\r\n\t\v\f]*)"#, options: []) +// this pattern checks for specific keys that won't have values +let re2 = try! NSRegularExpression(pattern: #"^(?:[ \t]*(?:\/\/)?[ \t]*@)(noframes)[ \t]*$"#, options: []) + // parser func parse(_ content: String) -> [String: Any]? { // returns structured data from content of file // will fail to parse if metablock or required @name key missing - let pattern = #"(?:(\/\/ ==UserScript==[ \t]*?\r?\n([\S\s]*?)\r?\n\/\/ ==\/UserScript==)([\S\s]*)|(\/\* ==UserStyle==[ \t]*?\r?\n([\S\s]*?)\r?\n==\/UserStyle== \*\/)([\S\s]*))"# - // force try b/c pattern is known to be valid regex - let regex = try! NSRegularExpression(pattern: pattern, options: []) let range = NSRange(location: 0, length: content.utf16.count) // return nil/fail if metablock missing - guard let match = regex.firstMatch(in: content, options: [], range: range) else { + guard let match = re0.firstMatch(in: content, options: [], range: range) else { logger?.debug("\(#function, privacy: .public) - Non matched content: \(content, privacy: .public)") return nil } @@ -158,12 +162,6 @@ func parse(_ content: String) -> [String: Any]? { var metadata = [:] as [String: [String]] // iterate through the possible metadata keys in file if let metas = Range(match.range(at: g2), in: content) { - let p = #"^(?:[ \t]*(?:\/\/)?[ \t]*@)([\w-]+)[ \t]+([^\s]+[^\r\n\t\v\f]*)"# - // this pattern checks for specific keys that won't have values - let p2 = #"^(?:[ \t]*(?:\/\/)?[ \t]*@)(noframes)[ \t]*$"# - // force try b/c pattern is known to be valid regex - let re = try! NSRegularExpression(pattern: p, options: []) - let re2 = try! NSRegularExpression(pattern: p2, options: []) // split metadatas by new line let metaArray = content[metas].split(whereSeparator: \.isNewline) // loop through metadata lines and populate metadata dictionary @@ -172,7 +170,7 @@ func parse(_ content: String) -> [String: Any]? { let metaString = String(meta).trimmingCharacters(in: .whitespaces) let range = NSRange(location: 0, length: metaString.utf16.count) // key lines not properly prefixed & without values will be skipped - if let m = re.firstMatch(in: metaString, options: [], range: range) { + if let m = re1.firstMatch(in: metaString, options: [], range: range) { // force unwrap key & value since matches regex above let key = metaString[Range(m.range(at: 1), in: metaString)!] let value = metaString[Range(m.range(at: 2), in: metaString)!] @@ -1027,6 +1025,9 @@ func stringToRegex(_ stringPattern: String) -> NSRegularExpression? { return regex } +// RE for parse matchPattern +let re3 = try! NSRegularExpression(pattern: #"^(http:|https:|\*:)\/\/((?:\*\.)?(?:[a-z0-9-]+\.)+(?:[a-z0-9]+)|\*\.[a-z]+|\*|[a-z0-9]+)(\/[^\s]*)$"#, options: .caseInsensitive) + func match(_ url: String, _ matchPattern: String) -> Bool { guard let parts = jsLikeURL(url), @@ -1052,10 +1053,8 @@ func match(_ url: String, _ matchPattern: String) -> Bool { if (ptcl != "http:" && ptcl != "https:") { return false } - let partsPattern = #"^(http:|https:|\*:)\/\/((?:\*\.)?(?:[a-z0-9-]+\.)+(?:[a-z0-9]+)|\*\.[a-z]+|\*|[a-z0-9]+)(\/[^\s]*)$"# - let partsPatternReg = try! NSRegularExpression(pattern: partsPattern, options: .caseInsensitive) let range = NSMakeRange(0, matchPattern.utf16.count) - guard let parts = partsPatternReg.firstMatch(in: matchPattern, options: [], range: range) else { + guard let parts = re3.firstMatch(in: matchPattern, options: [], range: range) else { logger?.error("\(#function, privacy: .public) - malformed regex match pattern - \(matchPattern, privacy: .public)") return false } From 06900223459036d2a238d93ab1af4d9f26cadbc7 Mon Sep 17 00:00:00 2001 From: ACTCD <101378590+ACTCD@users.noreply.github.com> Date: Sat, 10 Jan 2026 02:04:14 +0000 Subject: [PATCH 4/4] build(release): v4.8.6, v1.8.6 --- xcode/xcconfig/Userscripts-Release.xcconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcode/xcconfig/Userscripts-Release.xcconfig b/xcode/xcconfig/Userscripts-Release.xcconfig index 9555c1cc..573f4a9e 100644 --- a/xcode/xcconfig/Userscripts-Release.xcconfig +++ b/xcode/xcconfig/Userscripts-Release.xcconfig @@ -1,8 +1,8 @@ #include "Userscripts-Base.xcconfig" INFOPLIST_KEY_NSHumanReadableCopyright = Copyright © 2018–2026 Justin Wasack. All rights reserved. -IOS_APP_VERSION = 1.8.5 -MAC_APP_VERSION = 4.8.5 +IOS_APP_VERSION = 1.8.6 +MAC_APP_VERSION = 4.8.6 // Distribution CODE_SIGN_STYLE = Manual