Just to see things clear, the size of a stream is limited by the size of the database (some GBs or even TBs) and the size of the storage media (hdd,sdd), whichever smaller is (less the size of other globals). The size of a table row (all fields together) is limited by the string size (either 32KB or 3.6MB). In a table row you can put a REFERENCE of a stream but not the stream itself.

Just a wild guess... somewhere some character type checkbox is checked or not checked? Maybe a wrong driver type (ASCII/Unicode)?

// For example, IRIS sends a username "paul" as a two byte sequence
0x70 0x00 0x61 0x00 0x75 0x00 0x6C 0x00

// the other end sees
0x70 - this is a 'p'
0x00 - C-type end of string (hence, the name is just 'p')

But as I said, it's just a guess...

If you process thousands of time splits,  it's a good idea using %List or just a simple %String instead of JSON - you can have a time savings by factor of about 10!

/// old line
set res=[]
for t=t(0):int:t(1) do res.%Push($zdt(t\86400_","_(t#86400),3,1))
quit res

/// new line
set res=""
for t=t(0):int:t(1) set res=res_$lb($zdt(t\86400_","_(t#86400),3,1))
quit res

/// or
set res=""
for t=t(0):int:t(1) set res=res_","_$zdt(t\86400_","_(t#86400),3,1)
quit $e(res,2,*)

To see the differences, try loops like this

/// with JSON
s h=$zh f i=1:1:1E5 { s r=[] f j=1:1:10 { d r.%Push("abcd") } } w $zh-h,!

/// with %List
s h=$zh f i=1:1:1E6 { s r="" f j=1:1:10 { s r=r_$lb("abcd") } } w $zh-h,!

/// with %String
s h=$zh f i=1:1:1E6 { s r="" f j=1:1:10 { s r=r_","_"abcd" } } w $zh-h,!
/// start, end: timestamp format
/// int       : the interval in seconds
/// mod       : 0 = use the time value as is
///            +1 = round-up the timestamp to a multiple of <int>
///            -1 = round-down the timestamp to a multiple of <int>
///            
/// return an JSON array: [time1, time2, ... timeN]
/// 
ClassMethod Intervals(start, end, int = 15*60, mod = 0) As %DynamicArray
{
    set res=[], t(0)=$zdth(start,3,1), t(1)=$zdth(end,3,1)
    
    for i=0,1 {
        set t(i)=t(i)*86400+$p(t(i),",",2) 
        if mod,t(i)#int { set t(i)=t(i)-(t(i)#int) set:mod>0 t(i)=t(i)+int }
    }

    for t=t(0):int:t(1) do res.%Push($zdt(t\86400_","_(t#86400),3,1))
    quit res
}

You can edit (or enhance) the above code to give you all matching elements. Below I share a code with you where you can choose the result data type (%List or %String) and the result scope (all the matching elements or just the first match).

/// Find common items of two lists or in two delimited strings
/// (as used in a $piece-function)
/// 
/// itm1: first list (as %List or comma-delimited %String)
/// itm2: other list (as %List or comma-delimited %String)
/// ans : 0 = return a comma-delimited %String with the first match found
///       1 = return a comma-delimited %String with all matches found
///       2 = return a %List with the first match found
///       3 = return a %List with all matches found
///       
/// return value: according to <ans> argument
///       
/// Hint: the "$d(var)," part is only needed if the <itm1> argument is
///       of %List type and can contain an "undefined" element like the
///       second element in $lb(11,,33).
///       
ClassMethod FindCommonItems(itm1, itm2, ans = 0)
{
    set ptr=0, res="", all=ans#2
    set:'$lv(itm1) itm1=$lfs(itm1) set:'$lv(itm2) itm2=$lfs(itm2)
    
    while $listnext(itm1,ptr,val) { if $d(val),$lf(itm2,val) { set res=res_$lb(val) quit:'all } }
    quit $s(ans<2:$lts(res), 1:res)
}

There arises two questions.

First, why do you use a class where the documentation starts with "This class is used internally to hold property/field values needed for computed fields"?
Another word for classes which are marked as "used internally" is, that this classes can be changed (altered in its behavior) without any notice or documentation in one of the next versions. That's definitelly not what you want.
 
Second, what is your real problem (where you think, it's only solvable by using that %Library.PropertyHelper class)? Can you post (and describe) your real problem?

Two notes to your (above) code

- first, if you use the iterator method on an object which can contain long strings (longer what IRIS can handle) then you must specify the thrid argument to the %GetNext() method too, i.e.

while iterator.%GetNext(.key, .value, "stream") { ... }

- second, if you know the name and location of a stream property in an JSON object, like in your case, then just grab the data without using an iterator:

try { set jsonobj = {}.%FromJSON(httprequest.HttpResponse.Data) } catch { jsonobj=0 }
if jsonobj, jsonobj.statusCode = 200 {
   set pdf=jsonobj.value.labReport.%Get("pdf",,"stream<base64")  // according to the picture you provided
   ... // do something with the pdf-stream
}

For another example, how to work with long strings see this thread.

You have right, do {code} and do {code} while expr are mutually exclusive.

I thought more something like this do {{code}} (no spaces between curly braces) or, as you suggested, a new keyword like WRAP {...}, of course, BLOCK {...} and SCOPE {...} are also acceptable.

By the way, we already have an alternative for argumentless DO
do {code} while 0
It looks ugly and confusing and
- does not preseve $T
- does not establich a new stack level
but one can get over both easily respective do a work-around.

Oh, I see, at the very first use of a (new) private global,  the storage is decremented (on my  machine) by 768 bytes. I think, that bytes will hold some management data for each provate global (name).

Instance: cindy:ICINDY
Version : IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST

Username: kav
Password: ******
USER>s x=$storage

USER>set x=$storage, ^myKav=123 write x-$storage
768
USER>set x=$storage, ^myKav2=123 write x-$storage
768
USER>set x=$storage, ^myKav3=123 write x-$storage
768
USER>set x=$storage, ^myKav3=123 write x-$storage
0
USER>set x=$storage, ^myKav2=123 write x-$storage
0
USER>set x=$storage, ^myKav=123 write x-$storage
0
USER>

In general, $extract() and $zstrip() are your friends.
If you want to strip ONLY the LAST character, then use this

set data="abc,,"
set $extract(data,*)=""
write data --> abc,

If you want to strip ALL (same) trailing characters, use this

set remove=","
set data1="abc,"
set data2="abc,,,"
set data3="abc,,-,,"
set data1=$zstrip(data1,">",remove)
set data2=$zstrip(data2,">",remove)
set data3=$zstrip(data3,">",remove)

write data1 --> abc
write data2 --> abc
write data3 --> abc,,-

Maybe you didn't read it carefully enough...

/// the documentation say clearly:
///
/// %request.Data(itemName, itemIndex)
///
set vProfile1 = $Get(%request.Data("profile",1)) // the first value
set vProfile2 = $Get(%request.Data("profile",2)) // the second value
/// etc.

/// or you use a loop
///
kill value
ser value=0
set idx=$order(%request.Data("profile",idx))
while idx]"" {
    set value($increment(value))=%request.Data("profile",idx)
    set idx=$order(%request.Data("profile",idx)
}

/// value = item count
/// value(i) = i-th value

If you know which record is locked (i.e. ^My.Global(123) ) then you can identify the locking process (and therefore the user) in a simple method

Class DC.Lock Extends %RegisteredObject
{
/// For a given (global) reference
/// return the (exclusive) locking processID and username
/// 
/// ref: a global reference, for example: $name(^My.Global(1,2,3))
/// 
/// For other lock types (shared, remote)
/// use the infos obtained by info_types OWNER, MODE, FLAGS and COUNTS, see
/// https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_slock
/// 
ClassMethod Who(ref)
{
    if ^$LOCK(ref,"MODE")="X" {
        set pid=^$LOCK(ref,"OWNER")
        if pid {
            set job=##class(%SYS.ProcessQuery).%OpenId(pid)
            quit {"pid":(pid), "usr":($s(job:job.UserName,1:""))}
        }
        
    } else  { quit {} }
}
}

For example:

set ref=$name(^My.Global(123))
lock +@ref:1
if '$test {
    // in case, the node is locked,
    // check up, by who is the node locked
    set who=##class(DC.Lock).Who(ref)
    write who.%ToJSON() --> {"pid":"2396","usr":"kav"}
}