// // Temp_Bin_Str_Managed: C // // Determines if UTF8 conversion is needed for a series before it // is used with a byte-oriented function. // // If conversion is needed, a UTF8 series will be created. Otherwise, // the source series is returned as-is. // // Note: This routine should only be used to generate a value used // for temporary purposes, because it has a "surprising variance" // regarding its input. If the value's series can be reused, it is-- // and this depends on an implementation detail of internal encoding // that the user should not be aware of (they need not know if the // internal representation of an ASCII string uses 1, 2, or however // many bytes). But copying vs. non-copying means the resulting // data might or might not have previous values available to step // back into from the originating series! // // !!! Should performance dictate it, the callsites could be // adapted to know whether this produced a new series or not, and // instead of managing a created result they could be responsible // for freeing it if so. // REBSER *Temp_Bin_Str_Managed(const REBVAL *val, REBCNT *index, REBCNT *length) { REBCNT len = (length && *length) ? *length : VAL_LEN_AT(val); REBSER *series; assert(IS_BINARY(val) || ANY_STRING(val)); // !!! This used to check `len == 0` and reuse a zero length string. // However, the zero length string could have the wrong width. We are // expected to be returning a BYTE_SIZE() string, and that confused // things. It's not a good idea to mutate the source string (e.g. // reallocate under a new width) so consider having an EMPTY_BYTE_STRING // like EMPTY_ARRAY which is protected to hand back. // if ( IS_BINARY(val) || ( VAL_BYTE_SIZE(val) && All_Bytes_ASCII(VAL_BIN_AT(val), VAL_LEN_AT(val)) ) ){ // // It's BINARY!, or an ANY-STRING! whose codepoints are all values in // ASCII (0x00 => 0x7F), hence not needing any UTF-8 encoding. // series = VAL_SERIES(val); ASSERT_SERIES_MANAGED(series); if (index) *index = VAL_INDEX(val); if (length) *length = len; } else { // UTF-8 conversion is required, and we manage the result. series = Make_UTF8_From_Any_String(val, len, OPT_ENC_CRLF_MAYBE); MANAGE_SERIES(series); #if !defined(NDEBUG) // // Also, PROTECT the result in the debug build...because since the // caller doesn't know if a new series was created or if the initial // data is being used, they should not be modifying it! (We don't // want to protect the original data, because we wouldn't know when // we were allowed to unlock it...there's no later call in this // model to clean up the series.) { REBVAL protect; Val_Init_String(&protect, series); Protect_Value(&protect, FLAGIT(PROT_SET)); // just a string...not /DEEP...shouldn't need to Unmark() } #endif if (index) *index = 0; if (length) *length = SER_LEN(series); } assert(BYTE_SIZE(series)); return series; }
// // Clipboard_Actor: C // static REB_R Clipboard_Actor(struct Reb_Call *call_, REBSER *port, REBCNT action) { REBREQ *req; REBINT result; REBVAL *arg; REBCNT refs; // refinement argument flags REBINT len; REBSER *ser; Validate_Port(port, action); arg = DS_ARGC > 1 ? D_ARG(2) : NULL; req = cast(REBREQ*, Use_Port_State(port, RDI_CLIPBOARD, sizeof(REBREQ))); switch (action) { case A_UPDATE: // Update the port object after a READ or WRITE operation. // This is normally called by the WAKE-UP function. arg = OFV(port, STD_PORT_DATA); if (req->command == RDC_READ) { // this could be executed twice: // once for an event READ, once for the CLOSE following the READ if (!req->common.data) return R_NONE; len = req->actual; if (GET_FLAG(req->flags, RRF_WIDE)) { // convert to UTF8, so that it can be converted back to string! Val_Init_Binary(arg, Make_UTF8_Binary( req->common.data, len / sizeof(REBUNI), 0, OPT_ENC_UNISRC )); } else { REBSER *ser = Make_Binary(len); memcpy(BIN_HEAD(ser), req->common.data, len); SERIES_TAIL(ser) = len; Val_Init_Binary(arg, ser); } OS_FREE(req->common.data); // release the copy buffer req->common.data = 0; } else if (req->command == RDC_WRITE) { SET_NONE(arg); // Write is done. } return R_NONE; case A_READ: // This device is opened on the READ: if (!IS_OPEN(req)) { if (OS_DO_DEVICE(req, RDC_OPEN)) fail (Error_On_Port(RE_CANNOT_OPEN, port, req->error)); } // Issue the read request: CLR_FLAG(req->flags, RRF_WIDE); // allow byte or wide chars result = OS_DO_DEVICE(req, RDC_READ); if (result < 0) fail (Error_On_Port(RE_READ_ERROR, port, req->error)); if (result > 0) return R_NONE; /* pending */ // Copy and set the string result: arg = OFV(port, STD_PORT_DATA); len = req->actual; if (GET_FLAG(req->flags, RRF_WIDE)) { // convert to UTF8, so that it can be converted back to string! Val_Init_Binary(arg, Make_UTF8_Binary( req->common.data, len / sizeof(REBUNI), 0, OPT_ENC_UNISRC )); } else { REBSER *ser = Make_Binary(len); memcpy(BIN_HEAD(ser), req->common.data, len); SERIES_TAIL(ser) = len; Val_Init_Binary(arg, ser); } *D_OUT = *arg; return R_OUT; case A_WRITE: if (!IS_STRING(arg) && !IS_BINARY(arg)) fail (Error(RE_INVALID_PORT_ARG, arg)); // This device is opened on the WRITE: if (!IS_OPEN(req)) { if (OS_DO_DEVICE(req, RDC_OPEN)) fail (Error_On_Port(RE_CANNOT_OPEN, port, req->error)); } refs = Find_Refines(call_, ALL_WRITE_REFS); // Handle /part refinement: len = VAL_LEN(arg); if (refs & AM_WRITE_PART && VAL_INT32(D_ARG(ARG_WRITE_LIMIT)) < len) len = VAL_INT32(D_ARG(ARG_WRITE_LIMIT)); // If bytes, see if we can fit it: if (SERIES_WIDE(VAL_SERIES(arg)) == 1) { #ifdef ARG_STRINGS_ALLOWED if (!All_Bytes_ASCII(VAL_BIN_DATA(arg), len)) { Val_Init_String( arg, Copy_Bytes_To_Unicode(VAL_BIN_DATA(arg), len) ); } else req->common.data = VAL_BIN_DATA(arg); #endif // Temp conversion:!!! ser = Make_Unicode(len); len = Decode_UTF8(UNI_HEAD(ser), VAL_BIN_DATA(arg), len, FALSE); SERIES_TAIL(ser) = len = abs(len); UNI_TERM(ser); Val_Init_String(arg, ser); req->common.data = cast(REBYTE*, UNI_HEAD(ser)); SET_FLAG(req->flags, RRF_WIDE); } else // If unicode (may be from above conversion), handle it: if (SERIES_WIDE(VAL_SERIES(arg)) == sizeof(REBUNI)) { req->common.data = cast(REBYTE *, VAL_UNI_DATA(arg)); SET_FLAG(req->flags, RRF_WIDE); }