-
-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MultipartKit V5 #100
base: main
Are you sure you want to change the base?
MultipartKit V5 #100
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial comments.
@adam-fowler Do you want to take a look at this again and see if stuff makes more sense now? I added in some binary data (which even contains hex-CRLF 😄) to the tests so it should be able to parse anything now |
@Joannis @simonjbeaumont @czechboy0 pulling you into this so you can take a look if you want |
Sources/MultipartKit/FormDataDecoder/FormDataDecoder.SingleValueContainer.swift
Outdated
Show resolved
Hide resolved
public func decode<D: Decodable>(_ decodable: D.Type, from data: String, boundary: String) throws -> D { | ||
try decode(D.self, from: ByteBuffer(string: data), boundary: boundary) | ||
public func decode<D: Decodable>(_ decodable: D.Type, from string: String, boundary: String) throws -> D { | ||
try decode(D.self, from: Array(string.utf8), boundary: boundary) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes a copy from string
, can't we use withContiguousMemoryIfAvailable
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mhh I don't think that would work any better because we need the Collection there. We can't pass in the raw bytes and if we're copying them to an array we're still making a copy at that point right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're doing an .append
on the parser you're right that you're already making a copy. Except right now you're making two copies of the same data, and each time you're also allocating space for that data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I mean we can't pass the raw pointer to the decode method because it expects the collection of bytes, so this can't be done
try string.utf8.withContiguousStorageIfAvailable { bytes in
decode(D.self, from: bytes, boundary: boundary)
} ?? decode(D.self, from: Array(string.utf8), boundary: boundary)
And if we were to do something like
if let bytes = string.utf8.withContiguousStorageIfAvailable(Array.init) {
try decode(D.self, from: bytes, boundary: boundary)
} else {
try decode(D.self, from: Array(string.utf8), boundary: boundary)
}
we're still initialising an array with the raw bytes so I think this doesn't really save us a copy.
Unless you mean a different way of using withContiguousStorageIfAvailable
var currentBody = Body() | ||
|
||
// Append data to the parser and process the sections | ||
parser.append(buffer: data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary to copy data out before parsing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way we avoid a whole bunch of needMoreData
returns from the parser
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could it be restructured to be parseOrAppend
then?
if parserBuffer.isEmpty {
let result = parser.parse(data)
if result == .needMoreData {
parser.append(data)
}
} else {
parser.append(data)
parser.parse()
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why? We're just doing that once at the beginning, this is the sync parse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic parameter changes look good
Sources/MultipartKit/FormDataDecoder/FormDataDecoder+Decoder.swift
Outdated
Show resolved
Hide resolved
Sources/MultipartKit/FormDataEncoder/FormDataEncoder+Encoder.swift
Outdated
Show resolved
Hide resolved
Sources/MultipartKit/FormDataEncoder/FormDataEncoder+KeyedContainer.swift
Outdated
Show resolved
Hide resolved
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand it the user can receive a body as multiple MultipartSections if the underlying AsyncSequence has broken that body up. This is great as we don't want to pay the memory for large bodies if we can. But there are situations where we want to ensure we have a complete body section eg a block of data we want to run a JSON decode on. Is it possible to add a helper function to the Iterator to do this? eg Iterator.collectBody(upTo: memoryLimit)
which returns a header, complete body section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's currently a parse
method on MultipartParser
which loads the input all at once. I'm guessing you mean something of a middle ground between this and stream parsing? E.g just for one part of the message
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes just one part of the message eg I have a Multipart message with a zip file in there plus some metadata. I want to save the zip files to disk (using the least amount of memory possible ie streaming it) but parse the metadata with Codable so need the whole of it in memory.
No description provided.