gm2
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Portable bitwise operations library (was Re: Portability Considerati


From: Benjamin Kowarsch
Subject: Re: Portable bitwise operations library (was Re: Portability Considerations)
Date: Thu, 21 Mar 2024 02:47:46 +0900



On Wed, 20 Mar 2024 at 22:16, Alice Osako wrote:
https://github.com/Schol-R-LEA/Modula-2-Portable-Bit-Manipulation/tree/main

There has to be a better solution

Indeed, here is one:


I just don't understand why you don't want to use the code from my libraries.

Because I was unaware of them? I can see looking back on the earlier posts that you had linked to them, but I had assumed that they were specific to the new compiler project and you were using them as examples. I hadn't even looked at them until now.

Well, I had assumed you would at least have taken a look at the code to see what it is doing.

The code in the two github repos to which I had linked to
(1) https://github.com/m2sf/m2pp, and
(2) https://github.com/m2sf/m2bsk
is entirely written in Modula-2 (except for build configuration scripts).

Most of it is written in a least-common-denominator style, compatible between PIM and ISO:
https://github.com/m2sf/m2bsk/wiki/Coding-Standard
This comprises a naming convention and a list of features to avoid.

Wherever it was unavoidable to use dialect or compiler specific features, the repo contains multiple versions of the modules in question, one for each PIM and ISO, or one or more compiler specific versions.

The repos contain implementations, they are not examples.

These implementations were/are intended to be usable on just about any PIM and ISO M2 compilers still in use today, and equally important WITHOUT ANY USE of third libraries, including dialect or vendor specific libraries. The only prerequisites are the three libraries Terminal, Storage and FileSystem as described in Wirth's "Programming in Modula-2". Even so, there are C and ISO I/O library based replacement implementations of FileSystem.

https://github.com/m2sf/m2bsk/wiki

In other words, the code in these repos assumes absolutely nothing other than having some PIM or some ISO M2 compiler. All else needed to be provided by the project itself.

And precisely for this reason, it is a good place to go fishing for stuff when it comes to writing portable code. Changes are, whatever problem you are trying to solve portably, you will need some lower level function that's already in one of these repos. So, by all means, take advantage of it. ;-)

There is no need for neither casting nor loops.

The casting functions were actually unrelated to shift functions, being due to the use of the ISO functions for BIT and SETBIT (an additional pair of cast utilities were later added to handle conversions between CARDINAL and INTEGER for the ASHR function, actually). I will have to look at you solutions to see the alternatives.

You can test an individual bit easily by left shifting it into bit 0 and then use the built in ODD() function.

(* test bit *)
PROCEDURE bit ( n : CARDINAL; bitIndex : BitIndex ) : BOOLEAN;

BEGIN
  RETURN ODD(n DIV powerOf2[bitIndex])
END bit;

You can set and clear individual bits easily by using addition and subtraction.

(* set bit *)
IF NOT bit(n, bitIndex) THEN
  n := n + powerOf2[bitIndex]
END (* IF *)

(* clear bit *)
IF bit(n, bitIndex) THEN
  n := n - powerOf2[bitIndex]
END (* IF *)
 
For left shifting you can use multiplication by powers of 2, for right shifting integer division by powers of 2. The difference between mutiplication/division and shift operations is that the mathematical operations can overflow and the shift operations will ignore any overflow. Overflow on math operations will cause runtime errors, which we need to avoid. Thus when using multiplication and division for shifting, we first have to ensure that no overflow will happen by clearing any bits that would cause overflow prior to the operation. This is the principle upon which my library works.

The same approach can also be used for rotations. Then any shifted out bits need to be masked into the result at the opposite end. AND can be simulated by addition but where a bit is already 1, the addition of its corresponding bit in the mask is omitted by clearing it if it is also 1 as this would overflow into the next higher bit.

Since my code uses only basic integer math operations (*, DIV, +, -) it is actually very efficient, despite being portable.


Perhaps, I should say a thing or two about the use of ALL-CAPS in identifiers.

Modula-2 is a descendant of Algol and inherited an Algol practice called stropping. When the influential Algol-60 language report was developed, there still was no common character set and many platforms did not even have character sets with lowercase characters. In order to write the language report in a platform independent way, a convention was introduced by which code was represented in lowercase monospaced font, reserved words in boldface and built-in procedures underlined.

However, when actually writing Algol code, there was no boldface character set and thus reserved words needed to be distinguished from identifiers in some other way, which was implementation defined. Depending on the available characters on the platform each Algol compiler defined some characters as delimiters to mark a reserved word. The most common practice was to use the apostrophe as delimiter.

printed
if foo = bar then
became
'if' foo = bar 'then'
in the actual code.

This convention came to be called stropping.

When ASCII arrived on the scene and more and more platforms supported ASCII, some compilers used ALL-CAPS instead to strop reserved words and this wasn't just the case with Algol but also adopted by implementations of derivative languages.

One of the compilers/languages where ALL-CAPS were used to strop reserved words was Wirth's Algol-W. Wirth often restored omitted features in a later language, so ALL-CAPS stropping was absent in Pascal but brought back in Modula-2.

Thus, all those ALL-CAPS words you see in Modula-2 are actually meant to be lowercase boldface if they are reserved words, or lowercase italic or underlined if they are identifiers of built-in procedures/functions.

I had written a source code renderer for Modula-2 which is part of the Python Pygments framework and I added an Algol rendering mode to that which renders Modula-2 source code in this manner.

Thus, when writing a user library, one should try to avoid ALL-CAPS names. But if it is a runtime library for *built-in* procedures/functions, then ALL-CAPS names might be used so as to indicate that they are built-in.

The reason why I had listed the function names for bit operations in ALL-CAPS is that in M2R10 they are built-in, and the functions I listed are also built-in in various Pascal, Modula-2 and even Oberon compilers.

However, the portability libraries in the two github repos I linked to are user libraries for PIM and ISO which do not have these, and thus I used lowercase, title case and camel case names there.

regards
benjamin




reply via email to

[Prev in Thread] Current Thread [Next in Thread]