Skip to content

Commit cc8640e

Browse files
committed
Revert to manual UTF-16 loop for RPC strings to ensure stable memory and protocol alignment
1 parent a808c82 commit cc8640e

File tree

1 file changed

+16
-19
lines changed

1 file changed

+16
-19
lines changed

Sources/CosmoMSSQL/TDS/TDSRPCRequest.swift

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,13 @@ struct TDSRPCRequest {
6666
return buf
6767
}
6868

69-
// MARK: - Parameter writers
70-
7169
/// Write an NVARCHAR(MAX) parameter using PLP encoding.
7270
private func writeNVarCharMaxParam(name: String, value: String, into buf: inout ByteBuffer) {
7371
// B_VARCHAR name: 1-byte char count + UTF-16LE
7472
writeBVarChar(name, into: &buf)
7573
buf.writeInteger(UInt8(0)) // StatusFlags: normal
7674

7775
// TypeInfo: NVARCHAR (0xE7), maxLen=0xFFFF (MAX), 5-byte collation
78-
// LCID=1033 (0x409) | IgnoreCase bit (bit 20 = 0x100000) = 0x00100409 LE + sortId=0
7976
buf.writeInteger(UInt8(0xE7))
8077
buf.writeInteger(UInt16(0xFFFF), endianness: .little)
8178
buf.writeBytes([0x09, 0x04, 0x10, 0x00, 0x00]) // Latin1_General_CI_AS
@@ -145,22 +142,20 @@ struct TDSRPCRequest {
145142
buf.writeInteger(v.bitPattern, endianness: .little)
146143

147144
case .string(let v):
148-
// NVARCHAR(MAX) TypeInfo + PLP value (name & flags already written above)
145+
// NVARCHAR(MAX) TypeInfo + PLP value
149146
buf.writeInteger(UInt8(0xE7))
150147
buf.writeInteger(UInt16(0xFFFF), endianness: .little)
151148
buf.writeBytes([0x09, 0x04, 0x10, 0x00, 0x00]) // Latin1_General_CI_AS
152149
writePLPString(v, into: &buf)
153150

154151
case .decimal(let v):
155-
// Send as NVARCHAR string for exact representation
156152
let str = (v as NSDecimalNumber).stringValue
157153
buf.writeInteger(UInt8(0xE7))
158154
buf.writeInteger(UInt16(0xFFFF), endianness: .little)
159155
buf.writeBytes([0x09, 0x04, 0x10, 0x00, 0x00])
160156
writePLPString(str, into: &buf)
161157

162158
case .bytes(let v):
163-
// VARBINARY(MAX) with PLP
164159
buf.writeInteger(UInt8(0xA5))
165160
buf.writeInteger(UInt16(0xFFFF), endianness: .little)
166161
writePLPBytes(v, into: &buf)
@@ -172,7 +167,6 @@ struct TDSRPCRequest {
172167
writeUUIDMixedEndian(v, into: &buf)
173168

174169
case .date(let v):
175-
// DATETIME (0x3D) fixed 8 bytes: 4-byte days + 4-byte 1/300s ticks
176170
buf.writeInteger(UInt8(0x6F)) // DATETIMN
177171
buf.writeInteger(UInt8(8)) // maxLen
178172
buf.writeInteger(UInt8(8)) // actual len
@@ -188,17 +182,19 @@ struct TDSRPCRequest {
188182
// MARK: - PLP helpers
189183

190184
private func writePLPString(_ s: String, into buf: inout ByteBuffer) {
191-
let utf16Bytes = s.data(using: .utf16LittleEndian) ?? Data()
192-
let byteLen = utf16Bytes.count
185+
let utf16 = Array(s.utf16)
186+
let byteLen = utf16.count * 2
193187
if byteLen == 0 {
194-
buf.writeInteger(UInt64(0), endianness: .little) // empty (not null)
195-
buf.writeInteger(UInt32(0), endianness: .little) // terminator
188+
buf.writeInteger(UInt64(0), endianness: .little)
189+
buf.writeInteger(UInt32(0), endianness: .little)
196190
return
197191
}
198-
buf.writeInteger(UInt64(byteLen), endianness: .little) // total length
199-
buf.writeInteger(UInt32(byteLen), endianness: .little) // chunk length
200-
buf.writeBytes(utf16Bytes)
201-
buf.writeInteger(UInt32(0), endianness: .little) // terminator
192+
buf.writeInteger(UInt64(byteLen), endianness: .little)
193+
buf.writeInteger(UInt32(byteLen), endianness: .little)
194+
for unit in utf16 {
195+
buf.writeInteger(unit, endianness: .little)
196+
}
197+
buf.writeInteger(UInt32(0), endianness: .little)
202198
}
203199

204200
private func writePLPBytes(_ bytes: [UInt8], into buf: inout ByteBuffer) {
@@ -216,10 +212,11 @@ struct TDSRPCRequest {
216212
// MARK: - String helpers
217213

218214
private func writeBVarChar(_ s: String, into buf: inout ByteBuffer) {
219-
let utf16Bytes = s.data(using: .utf16LittleEndian) ?? Data()
220-
let charCount = utf16Bytes.count / 2
221-
buf.writeInteger(UInt8(charCount))
222-
buf.writeBytes(utf16Bytes)
215+
let utf16 = Array(s.utf16)
216+
buf.writeInteger(UInt8(utf16.count))
217+
for unit in utf16 {
218+
buf.writeInteger(unit, endianness: .little)
219+
}
223220
}
224221

225222
// MARK: - UUID mixed-endian (SQL Server format)

0 commit comments

Comments
 (0)