Create BSTR from ASCIIZ PTR in PowerBasic sqlite3 Wrapper - sqlite

I'm building a small wrapper for some sqlite3 functions.
sqlite3.dll has a function sqlite3_column_text16() that returns a UTF-16 string. This seems like the right function to call since PowerBasic and Windows (and OLE) use UTF-16 natively (called a BSTR), however, when I do this:
LOCAL pzValue AS ASCIIZ PTR
LOCAL ValLen AS LONG
LOCAL ColText AS STRING
pzValue = sqlite3_column_text16(ppStmt, 0) 'Returns column text ("Russ")
ValLen = sqlite3_column_bytes16(ppStmt, 0) 'Returns the byte length of column text (8)
ColText = &pzValue 'Assign a ASCIIZ PTR to a STRING - ISSUE HERE[/CODE]
ColText only contains the first letter of the UTF-16 string.
This makes sense (I think) because it's a UTF-16 string, so the first byte is valid ASCII, but the second byte is zero (null), so in the assignment ColText = &pzValue, ColText is assigned all the text up to the first null character. In this case, the string is "Russ", so the bytes in the UTF-16 are "R0u0s0s0" (each ASCII character is followed by a null (0).
So, how do I convert this UTF-16 string to a PowerBasic STRING (which is a BSTR)?
I checked some sqlite3 wrappers and found this code (SQLiteningServer.bas):
llColLength = sqlite3_column_bytes(rhSet, llDo)
if llColLength and llColLength <= 253 then
    ' It is not weird
    lsaColumns(llDo) = mkbyt$(llColLength) & peek$(sqlite3_column_blob(rhSet, llDo), llColLength)
elseif llColLength = 0 then
    ' Its zero so check type for NULL
    if sqlite3_column_type(rhSet, llDo) = 5 then
        lsaColumns(llDo) = mkbyt$(254)
    else
        lsaColumns(llDo) = mkbyt$(0)
    end if
else
    ' It is too long so add a Long
    lsaColumns(llDo) = mkbyt$(255) & mkl$(llColLength) & peek$(sqlite3_column_blob(rhSet, llDo), llColLength)
end if
So, in this code they are doing this:
lsaColumns(llDo) = mkbyt$(llColLength) & peek$(sqlite3_column_blob(rhSet, llDo), llColLength)
Which almost looks like they are constructing a BSTR in memory, but not exactly. To be a valid BSTR, the leading numeric value should be an INT16 indicating the byte length of the string, but in the code above, they are using a leading BYTE, so it's not a valid BSTR, right? I don't know.
How do I take a UTF-16 string returned by sqlite3_column_text16(ppStmt, 0) and convert it to a PowerBasic STRING (which is a standard OLE BSTR).

A BSTR is a pointer to a string of wide characters followed by a null character. The word preceding the text (at pointer -2) is the length of the string.
Look at SysAllocString(...) and SysAllocStringLen(...) for simple methods of creating a BSTR from your UTF-16 text

Related

Error, missing operation after conditional break

I am trying to run this procedure, but am getting an "Error, missing operation", highlighting the conditional break:
is_prime := proc()
    local i, x_check;
    printf(`Enter an integer to check if it is a prime number...`);
    x_check:=parse(readline(terminal));
    if x_check<=2 then
        printf(cat(x_check,` is a prime number.`));
    else
        for i from 2 to x_check-1 do
              if (irem(x_check,i)=0) then
                 printf(cat(x_check,` is not a prime number.`));
                 break 2;
            end if;
          end do;
        printf(cat(x_check,` is a prime number.`));
     end if;
end proc:
If I don't include the integer, I am able to run the procedure, but it does not function like I would like it to. I am running Maple 2022, and can successfully run the example conditional break code in the Help. What is wrong with my use of break?
I am unfamiliar with conditional break, so beyond removing the integer, I do not know what else to try. I wanted to exit out of the outer if statement, and thought break would be able to do this so that my last printf statement is not displayed.
The break functionality is for do-loops, not if-then.
So your expectation of its breaking out of the your outer if-then is not reasonable.
The reason that you're getting an error message is that you attempted break 2 within only a single level (ie. unnested) do-loop. So it's invalid to instruct it there to break out of two levels of looping.
Since there's nothing else that you don't want to happen at the stage in question, you could get by with just return at that juncture.
It's not best practice to use name-quotes (single left-ticks) inside printf, for the English text. Those are just names, and in the worst scenario might even have been assigned some value. (Unlikely, but let's be careful.) Instead, the printf command offers its own neat syntax for formatting its message, using safe strings. Let's do that below.
And, for fun, I changed your readline to a readstat, to get a more meaningful title in the popup query.
is_prime := proc()
local i, x_check;
x_check:=readstat("Enter an integer to test its primality");
if not type(x_check,posint) then return
elif x_check<=2 then
printf("%a is a prime number.\n",x_check);
else
for i from 2 to x_check-1 do
if (irem(x_check,i)=0) then
printf("%a is not a prime number.\n",x_check);
return;
end if;
end do;
printf("%a is a prime number.\n",x_check);
end if;
end proc:
is_prime();
ps. Do you want to handle the input 1 differently?

How to lowercase a string in CLEAN

I have a problem in CLEAN, how can I make lowercase all letter in a string? I can do it through an char array, but i need to do it with a string too.
I have the code below so far:
module Something
import StdEnv, StdLib
arrayLower:: [Char] -> [Char]
arrayLower[x:xs] = (map toLower [x:xs])
stringLower:: String -> String
stringLower_ = ""
stringLowers = toString (arrayLower s)
Start:: String
Start = stringLower"SSSsss"
Your first case
stringLower _ = ""
means that stringLower applied to anything is the empty string.
I'm surprised that you didn't get a warning for the redundant second case.
A String is an array (unboxed, so it's a {#Char}), and you say that you already know how to do this with arrays, but your arrayLower is defined for lists of Char ([Char]), not arrays.
This, using an array comprehension, works for me:
stringLower :: String -> String
stringLower s = {toLower c \\ c <-: s}

how to convert an array of characters to qstring?

I have an array -
char name[256];
sprintf(name, "hello://cert=prv:netid=%d:tsid=%d:pid=%d\0", 1010,1200, 1300);
QString private_data_string = name;
At the last offset of this string i.e. '\0',when I try to do the following.
while(private_data_string.at(offset) != ':' &&
private_data_string.at(offset) != ';' &&
private_data_string.at(offset).isNull() == false)
The application aborts. Looks like that the data pointer is also zero at the string '\'. How can I fix this?
QString doesn't contain terminating character as you expect that is why you are failing assertion out of bounds. This is proper approach:
while(offset<private_data_string.length() &&
private_data_string.at(offset) != ':' &&
private_data_string.at(offset) != ';') {
// ...
}
It looks like you are doing something strange. Looks like your question is wrong. You are asking how to fix your strange solution of some mysterious problem, instead explain what are you trying to do and then as a bonus: how did you try to solve it.
You need to know several facts:
Writing \0 at tge end of your string literal is not necessary. String literals are null-terminated by default. Literal "abc" will actually contain 4 characters including terminating null character. Your string literal has 2 null characters at its end.
You have used the default constructor QString(char*). There is no additional data about buffer's length, so QString reads characters from the buffer until it encounters first null character. It doesn't matter how many null characters are actually at the end. The null character is interpreted as a buffer end marker, not a part of the string.
When you have QString "abc", its size is 3 (it would be surprising to have another value). Null character is not a part of the string. QString::at function can be used for positions 0 <= position < size(). This is explicitly specified in the documentation. So it doesn't matter if QString's internal buffer is null-terminated or not. Either way, you don't have access to null terminator.
If you really want null character to be part of your data, you should use QByteArray instead of QString. It allows to specify buffer size on construction and can contain as many null characters as you want. However, when dealing with strings, it's usually not necessary.
You should use QString::arg instead of sprintf:
QString private_data_string =
QString("hello://cert=prv:netid=%1:tsid=%2:pid=%3")
.arg(netid).arg(tsid).arg(pid);
sprintf is unsafe and can overrun your fixed-size buffer if you're not careful. In C++ there's no good reason to use sprintf.
"A QString that has not been assigned to anything is null, i.e., both the length and data pointer is 0" - this has nothing to do with your situation because you have assigned a value to your string.

Pass a string value in a recursive bison rule

i'm having some issues on bison (again).
I'm trying to pass a string value between a "recursive rule" in my grammar file using the $$,
but when I print the value I have passed, the output looks like a wrong reference ( AU�� ) instead the value I wrote in my input file.
line: tok1 tok2
| tok1 tok2 tok3
{
int len=0;
len = strlen($1) + strlen($3) + 3;
char out[len];
strcpy(out,$1);
strcat(out," = ");
strcat(out,$3);
printf("out -> %s;\n",out);
$$ = out;
}
| line tok4
{
printf("line -> %s\n",$1);
}
Here I've reported a simplified part of the code.
Giving in input the token tok1 tok2 tok3 it should assign to $$ the out variable (with the printf I can see that in the first part of the rule the out variable has the correct value).
Matching the tok4 sequentially I'm in the recursive part of the rule. But when I print the $1 value (who should be equal to out since I have passed it trough $$), I don't have the right output.
You cannot set:
$$ = out;
because the string that out refers to is just about to vanish into thin air, as soon as the block in which it was declared ends.
In order to get away with this, you need to malloc the storage for the new string.
Also, you need strlen($1) + strlen($3) + 4; because you need to leave room for the NUL terminator.
It's important to understand that C does not really have strings. It has pointers to char (char*), but those are really pointers. It has arrays (char []), but you cannot use an array as an aggregate. For example, in your code, out = $1 would be illegal, because you cannot assign to an array. (Also because $1 is a pointer, not an array, but that doesn't matter because any reference to an array, except in sizeof, is effectively reduced to a pointer.)
So when you say $$ = out, you are making $$ point to the storage represented by out, and that storage is just about to vanish. So that doesn't work. You can say $$ = $1, because $1 is also a pointer to char; that makes $$ and $1 point to the same character. (That's legal but it makes memory management more complicated. Also, you need to be careful with modifications.) Finally, you can say strcpy($$, out), but that relies on $$ already pointing to a string which is long enough to hold out, something which is highly unlikely, because what it means is to copy the storage pointed to by out into the location pointed to by $$.
Also, as I noted above, when you are using "string" functions in C, they all insist that the sequence of characters pointed to by their "string" arguments (i.e. the pointer-to-character arguments) must be terminated with a 0 character (that is, the character whose code is 0, not the character 0).
If you're used to programming in languages which actually have a string datatype, all this might seem a bit weird. Practice makes perfect.
The bottom line is that what you need to do is to create a new region of storage large enough to contain your string, like this (I removed out because it's not necessary):
$$ = malloc(len + 1); // room for NUL
strcpy($$, $1);
strcat($$, " = ");
strcat($$, $3);
// You could replace the strcpy/strcat/strcat with:
// sprintf($$, "%s = %s", $1, $3)
Note that storing mallocd data (including the result of strdup and asprintf) on the parser stack (that is, as $$) also implies the necessity to free it when you're done with it; otherwise, you have a memory leak.
I've solved it changin the $$ = out; line into strcpy($$,out); and now it works properly.

Pypy: Why does a "simpler" nested list take longer to flatten?

I had the need to flatten some nested lists. After writing the flatten function, I naturally tried to see how many ways I could break it. I finally ran it through pypy and was delighted to discover that when the lists got really deep pypy was running significantly faster than cpython.
However, I'm seeing a strange situation where a test with a larger, more complicated list, with a larger variety of elements is actually executing faster than the "simpler" list.
Test 1, which has fewer elements, is consistently about a second slower to run (using time pypy ./script.py) than test 2.
def flatten(lol):
if not any([isinstance(i, list) for i in lol]):
return lol
flatter = [i for i in lol if not isinstance(i, list)]
for sublist in lol:
if isinstance(sublist, list):
flatter.extend(sublist)
return flatten(flatter)
def silly(l):
return [l, [l]]
nested_function = [["kaplutis", ["mucus", ["my brain", ["is","filled",["with",["pus",]]]]]], "santa gertrudis",[[[[["innermost",flatten(["in", "func"])]]]]]]
tuples = ["empty tuple retained-->", (), ("2-tuple not flattened",), ("I'm the only thing in this tuple")]
dicts = [{"hip": "hop", "ster": "toad"}]
lone_dict = {"lone": 1, "dict": 2}
silly_list = ["1"]
for i in range(20):
silly_list = silly(silly_list)
# test 1 - only the absurdly nested list
print(len(flatten(silly_list)))
# test 2 - absurdly nested list, with
lol = [nested_function, tuples, dicts, lone_dict, silly_list]
print(len(flatten(lol)))
The only thing I can figure is I'm running into some accidental optimization when the JIT is dealing with the simpler nested lists before "silly_list" in the second test.
this particular flatten function did hit various bad perf points in cpython and pypy
we figured it on irc some time ago and the fix was to create a completely different flatten function that was faster in cpython and much faster on pypy
i don't remember the url, it would be nice if OP would provide
the basic code was along the lines of
def flatten(items, akk=None)
if akk is None:
akk = []
for item in items:
if isinstance(item, list):
flatten(item, akk)
else:
akk.append(item)
an answer instead of a comment to include the code
It is hard to understand what your code does. Make sure it is correct. Optimize later. You could test the results against:
def flatten(lst, nonscalar_types=list):
    for x in lst:
        if isinstance(x, nonscalar_types):
            yield from flatten(x) # pep 380
        else:
            yield x # non-list type
Example
print(sum(1 for _ in flatten(lol)))

Resources