ENHANCED: Faster format/3 for binary streams.
This speeds up web servers considerably when sending binary files.
This commit is contained in:
@@ -229,6 +229,7 @@ pub enum SystemClauseType {
|
|||||||
PeekCode,
|
PeekCode,
|
||||||
PointsToContinuationResetMarker,
|
PointsToContinuationResetMarker,
|
||||||
PutByte,
|
PutByte,
|
||||||
|
PutBytes,
|
||||||
PutChar,
|
PutChar,
|
||||||
PutCode,
|
PutCode,
|
||||||
REPL(REPLCodePtr),
|
REPL(REPLCodePtr),
|
||||||
@@ -415,6 +416,9 @@ impl SystemClauseType {
|
|||||||
&SystemClauseType::PutByte => {
|
&SystemClauseType::PutByte => {
|
||||||
clause_name!("$put_byte")
|
clause_name!("$put_byte")
|
||||||
}
|
}
|
||||||
|
&SystemClauseType::PutBytes => {
|
||||||
|
clause_name!("$put_bytes")
|
||||||
|
}
|
||||||
&SystemClauseType::PutChar => {
|
&SystemClauseType::PutChar => {
|
||||||
clause_name!("$put_char")
|
clause_name!("$put_char")
|
||||||
}
|
}
|
||||||
@@ -552,6 +556,9 @@ impl SystemClauseType {
|
|||||||
("$put_byte", 2) => {
|
("$put_byte", 2) => {
|
||||||
Some(SystemClauseType::PutByte)
|
Some(SystemClauseType::PutByte)
|
||||||
}
|
}
|
||||||
|
("$put_bytes", 2) => {
|
||||||
|
Some(SystemClauseType::PutBytes)
|
||||||
|
}
|
||||||
("$put_char", 2) => {
|
("$put_char", 2) => {
|
||||||
Some(SystemClauseType::PutChar)
|
Some(SystemClauseType::PutChar)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -367,8 +367,20 @@ format(Fs, Args) :-
|
|||||||
format(Stream, Fs, Args) :-
|
format(Stream, Fs, Args) :-
|
||||||
phrase(format_(Fs, Args), Cs),
|
phrase(format_(Fs, Args), Cs),
|
||||||
( stream_property(Stream, type(binary)) ->
|
( stream_property(Stream, type(binary)) ->
|
||||||
maplist(char_code, Cs, Bytes),
|
% maplist(char_code, Cs, Bytes) is currently a lot slower
|
||||||
maplist(put_byte(Stream), Bytes)
|
% than first converting Cs to an atom, and then to codes.
|
||||||
|
% In the future, we can ideally avoid creating an atom here,
|
||||||
|
% since an atom leaves traces in the system.
|
||||||
|
atom_chars(A, Cs),
|
||||||
|
atom_codes(A, Bytes),
|
||||||
|
( member(NonByte, Bytes), NonByte > 255 ->
|
||||||
|
char_code(Char, NonByte),
|
||||||
|
throw(error(representation_error(Char), format/3))
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
% For binary streams, we use a specialised internal predicate
|
||||||
|
% that uses only a single "write" operation for efficiency.
|
||||||
|
'$put_bytes'(Stream, Bytes)
|
||||||
; maplist(put_char(Stream), Cs)
|
; maplist(put_char(Stream), Cs)
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|||||||
@@ -2153,6 +2153,37 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&SystemClauseType::PutBytes => {
|
||||||
|
let mut stream =
|
||||||
|
self.get_stream_or_alias(self[temp_v!(1)], indices, "$put_bytes", 2)?;
|
||||||
|
|
||||||
|
let stub = MachineError::functor_stub(clause_name!("$put_bytes"), 2);
|
||||||
|
let bytes = self.integers_to_bytevec(temp_v!(2), stub);
|
||||||
|
|
||||||
|
match stream.write(&bytes) {
|
||||||
|
Ok(_) => {
|
||||||
|
return return_from_clause!(self.last_call, self);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let stub = MachineError::functor_stub(
|
||||||
|
clause_name!("$put_bytes"),
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
|
||||||
|
let addr = self.heap.to_unifiable(
|
||||||
|
HeapCellValue::Stream(stream.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(self.error_form(
|
||||||
|
MachineError::existence_error(
|
||||||
|
self.heap.h(),
|
||||||
|
ExistenceError::Stream(addr),
|
||||||
|
),
|
||||||
|
stub,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
&SystemClauseType::GetByte => {
|
&SystemClauseType::GetByte => {
|
||||||
let mut stream =
|
let mut stream =
|
||||||
self.get_stream_or_alias(self[temp_v!(1)], indices, "get_byte", 2)?;
|
self.get_stream_or_alias(self[temp_v!(1)], indices, "get_byte", 2)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user