pperl 0.6.22#

2026-10-06 to 2026-10-11, 89 commits.

Language and runtime#

  • %Config is complete: it has every key perl 5.44’s has, 1,261 of them, where it had 198. The d_* and i_* answers come from probing the C library and include tree the way Configure does, the type sizes and formats from the build target, the tools from PATH, and the version, signal and extension keys from the running interpreter. ExtUtils::MakeMaker no longer warns about uninitialized values on every WriteMakefile, DirHandle, Net::protoent and Net::servent no longer believe their functions are missing, and $Config{PERL_VERSION}, $Config{d_readdir}, $Config{uidsize} and the rest exist.

  • The library’s data files are in the binary and readable: the files perl’s modules open by path beside themselves, such as ExtUtils::Manifest’s default MANIFEST.SKIP and the unicore tables Unicode::UCD reads. make distdir no longer copies MYMETA.* into the distribution directory, and Unicode::UCD’s charblock, charscript and friends no longer depend on a perl installed on the host. @INC starts with ./pperl-native, the embedded tree’s own entry.

  • The daemon and duplicated output handles: a script run through the daemon with PPERL_DAEMON=2 that duplicates STDOUT, STDERR or STDIN while it compiles - every script that loads Test::More does - printed its output only on the first run, and the program reading that first run’s output never saw it end. Each run now writes to its own output and reads its own input.

  • $0 and the environment: assigning a name longer than the command line to $0 no longer destroys the environment that child processes inherit. A program that renamed itself and then ran system, a backtick or exec had its children start with almost no environment (no HOME, no PATH additions), while its own %ENV looked fine.

JIT and parallelization#

  • Conditional expressions in compiled loops: a ?: whose arm reads an element through a reference ($c ? $x->[$i] : 0) or from a hash ($c ? $h{$k} : 0) no longer makes the loop leave compiled code each time that arm is taken. An arm that is an element of a plain array already stayed compiled.

  • Recursive subs called from compiled loops: a loop such as for (1 .. $n) { local $_; $sum += fib($k) } died with panic: pp_iter, type=9 once it compiled, when the sub it calls recurses; Benchmark’s timethese and countit of a recursive sub died the same way. The compiled loop mistook a deeper recursion level’s frame for its own.

  • A regex run once no longer gets compiled: the regex compiler decided by the size of the string a pattern was matched against, so a pattern used a single time on a large string, as charnames::viacode and vianame do for every lookup over the one-megabyte names table, was compiled for that one match and the compilation cost several times the match. A place in the program that builds a new pattern for every lookup and runs each once now compiles none of them after the first; a loop of a thousand charnames lookups runs in a fifth of the instructions, and a pattern that is reused compiles when it did.

  • Integers past 2^53 in compiled loops: a loop that computes an integer beyond 9007199254740992 and divides by it next, such as the long division inside Math::BigInt on numbers held as floats (Math::BigFloat->new(2)->bpow(240)->broot(9) with Math::BigInt::FastCalc), could get one quotient digit wrong the first time the loop ran on float-typed values after integer-typed ones; later runs were right. The compiled loop had taken a rounded copy of the exact integer perl kept. It now leaves compiled code for that pass, as it does whenever a value cannot be held exactly.

  • $_ in a nested statement-modifier loop: in a compiled loop that contains EXPR for LO .. HI, where EXPR calls a function and reads $_ in its arguments, the first pass of every inner loop saw $_ undefined: a “Use of uninitialized value $_” warning, and 0 where the value was wanted, so a range not starting at 0 gave wrong results.

Modules#

  • Native modules in the release build: the build’s profile-guided optimisation now runs POSIX, Socket, Errno, Config, Time::Piece, Sys::Syslog, IPC::SysV, the DBM modules, Unicode::Normalize, Unicode::UCD, charnames, the Symbol and EBCDIC encodings, the :encoding, :via and :mmap layers, File::Map, Hash::Util, Clone, Memoize, Math::BigInt::FastCalc, B, Opcode, Safe, PadWalker, Devel::Peek, mro, attributes, source filters, EV, Event, Coro’s loop bridges, Peta::Dict, Peta::PCRE2, Peta::XS, Peta::FFI and Peta::LMDB before it decides what is hot. None of these had been run by the build before, so the release binary carried them compiled for size rather than for speed.

  • Errno has every name the host defines: %!, the constants and @Errno::EXPORT_OK now come from the errno.h of the machine pperl was built on and the headers it includes, read at build time the way perl’s own Errno.pm is generated. EFTYPE, EFSBADCRC and EFSCORRUPTED, which current kernel headers define and the previous fixed table lacked, exist; on this host the list is identical to perl 5.44’s, all 137 names.

  • The -C switch: pperl -CSAD script and the other forms of perl’s Unicode switch work, alone and at the end of a cluster such as -wCSD; they were refused with “Unknown option: -C”, and only PERL_UNICODE in the environment had the effect.

  • Compress::Raw::Bzip2 exists, with Compress::Raw::Bunzip2 and the BZ_* constants, over the system’s libbz2. With it IO::Compress::Bzip2, IO::Uncompress::Bunzip2, IO::Uncompress::AnyUncompress and Archive::Tar read and write bzip2 data, .tbz archives among it, and streamzip makes bzip2 zip entries; they died with “Can’t locate Compress/Raw/Bzip2.pm”.

  • Peta::ReadLine is the name of the readline module; Term::ReadLine::Gnu is an alias of it, so use Term::ReadLine still picks it up, a program that tests $term->ReadLine eq "Term::ReadLine::Gnu" (CPAN.pm, the debugger) still passes, and Term::ReadLine::Gnu::XS::* calls still work. Peta::ReadLine->new reports Peta::ReadLine, version 0.2.0; the alias reports the name it was asked by.

  • Peta::ReadLine’s function table and keymaps: get_all_function_names lists the bindable commands (it returned one name of garbage), get_function_name finds a function’s name, and $attribs->{executing_keymap} and $attribs->{binding_keymap} are Keymap objects that get_keymap_name and the binding methods accept. import Term::ReadLine::Gnu qw(:keymap_type) and the other export tags work under the alias as they do under Peta::ReadLine.

  • Peta::ReadLine’s ignore_some_completions_function: a completion with $attribs->{ignore_some_completions_function} set no longer dies with “panic: attempt to copy freed scalar” in the statement that called complete_internal or call_function.

  • Peta::ReadLine has its own line editor: no readline, history or curses library is loaded any more, so line editing works where none is installed and in the static build. It behaves as GNU Readline 8.3 does - commands, key bindings, the init file, history, completion, signals, and the same screen after every key. In a UTF-8 locale the commands move, delete, change case and search by characters, not bytes. It identifies itself: $attribs->{library_version} is 8.3 (Peta::ReadLine 0.2.0) and $attribs->{gnu_readline_p} is 0.

  • Imager follows Imager 1.037. getsamples checks its arguments itself and says in errstr why a call failed (a row outside the image, a channel the image does not have); a failed call returns the empty list, or undef in scalar context. An image too large to allocate fails Imager->new with a message instead of ending the program, and set_out_of_memory_handler exists.

  • Imager drawing and images: box, line (plain and anti-aliased) and arc draw; getscanline, setscanline, the palette rows and setcolors work; images convert between 8-bit, 16-bit and double samples and can be compared. open_log, log and close_log write Imager’s log, and colours have red, green, blue, alpha and HSV conversion. Three results changed to what Imager gives: a 16-bit image read as 8-bit colours was one too bright for some values, i_img_info’s fourth value is the channel mask, and the colour count of a direct image is undef.

  • Imager I/O objects: Imager::IO exists: new_buffer, new_fd, new_fh, new_cb and new_bufchain (and the io_new_* functions) make an I/O object over a string, a file descriptor, a file handle, Perl callbacks or a file in memory, with read, write, seek, getc, gets, peekn, close and the rest of the class. io_slurp of an object that is not a memory file dies with a message; under Imager itself it ends the program silently.

  • Imager PNM files: Imager reads and writes PNM files (PBM, PGM and PPM, ASCII and binary, 8 and 16 bit), read_multi included, so Imager->new(file => 'x.ppm') and $img->write(type => 'pnm') work. set_file_limits, get_file_limits and check_file_limits bound what is read, and the format of a file is detected from its first bytes, with add_file_magic for signatures of your own.

  • Imager raw files: read and write with type => 'raw' work: rows of bare 8-bit samples, interleaved by sample or by line, with raw_datachannels and raw_storechannels choosing which channels of the file are kept.

  • Imager BMP files: Windows BMP files are read at 1, 4, 8, 16, 24 and 32 bits a pixel, run length encoded and with bit-field masks included, and written uncompressed. A grey image with an alpha channel is written with its own grey; under Imager itself each pixel takes its neighbour’s value as alpha and half the row is garbage.

  • Imager TGA files: Targa files are read and written: colour-mapped, direct colour and grey, run length encoded or not, at 8, 15, 16, 24 and 32 bits a pixel, with the compress, wierdpack and idstring options. %Imager::formats now lists what Imager lists without any library: raw, pnm, bmp, tga and ifs.

  • Imager masked images: $img->masked gives a view of part of an image, optionally written through a mask, that can be drawn into, copied and written to a file. setsamples is available, with its floating point and 16-bit forms, and so are getsamples with type => 'float' and type => '16bit'. A floating point sample outside 0 to 1 now wraps when stored in an 8- or 16-bit image, as in Imager, and getmask on a fresh image is -1.

  • Imager fills: box and arc take a fill, and Imager::Fill makes solid, hatched, image and opacity fills with the twelve combine modes (normal, multiply, add, darken, hue and the rest).

  • Imager paste and crop: $img->paste copies a rectangle of another image in, converting between sample depths and channel counts, and $img->crop cuts one out.

  • Imager circles and flood fill: circle draws filled anti-aliased circles and, with filled => 0, outlines, plain or anti-aliased; arc with filled => 0 draws the outline of an arc. flood_fill fills the region around a point with a colour or a fill, optionally up to a border colour, and polybezier draws a Bezier curve.

  • Imager polygons: polygon and polypolygon fill anti-aliased polygons with a colour or a fill, by the even-odd or the non-zero winding rule, and arc with aa => 1 draws an anti-aliased pie slice.

  • Imager rubthrough and compose: $img->rubthrough lays another image over the image by its alpha channel, and $img->compose does so with a combine mode, an opacity and optionally a mask image.

  • Imager fountain fills: Imager::Fill->new(fountain => ...) makes a gradient fill (linear, bilinear, radial, square, revolution or conical, with repeats, segments and super-sampling), and $img->filter(type => 'fountain') draws one over a whole image.

  • Imager channel operations: $img->convert makes a new image by a matrix or a preset (grey, noalpha, rgb, addalpha and the single channels), Imager->combine makes one from channels of several images, $img->map runs channels through lookup tables, and $img->flip mirrors the image.

  • Imager quantisation: $img->to_paletted turns a direct colour image into a paletted one, with the palette from median cut, the addi method, a fixed map or your own colours, and the pixels matched by closest colour, perturbation or error diffusion. Imager->make_palette, getcolorcount, getcolorusage and getcolorusagehash work.

  • Imager scaling: $img->scale, scaleX and scaleY return a scaled copy, with the normal, preview and mixing methods.

  • Imager rotation: $img->rotate turns an image by quarter turns (right) or by any angle (degrees, radians, with back), and $img->matrix_transform maps it through a 3 x 3 matrix.

  • Imager trim: $img->trim and trim_rect find and remove the border of an image, by colour ranges (colors, auto) or by transparency (alpha).

  • Imager transform and transform2: Imager::transform2 computes an image from an expression per pixel (rpnexpr, or expr with Parse::RecDescent), reading up to three input images, and $img->transform moves pixels by two stack programs.

  • Imager filters: $img->filter has every filter Imager has built in: autolevels, autolevels_skew, bumpmap, bumpmap_complex, contrast, gaussian, gaussian2, gradgen, hardinvert, hardinvertall, mosaic, nearest_color, noise, postlevels, radnoise, turbnoise, unsharpmask and watermark, beside conv and fountain. $img->difference and rgb_difference show where two images differ.

  • Imager object destructors: calling the DESTROY of an Imager class on something that is not a reference dies with Imager’s message, “is not a reference”; it named an expected type instead. $Imager::_context exists.

  • Imager plugin loader: Imager->load_plugin, which loads filter plugins compiled from C, returns undef and sets $Imager::ERRSTR instead of dying with “Undefined subroutine”. Such plugins cannot be loaded; Imager has documented that interface as deprecated since 2006.

  • Peta::WebGPU and fork: a process that has opened a device can fork. The child used to hang for ever when it exited; now it ends normally. Using an inherited device, buffer or shader in the child dies with a message that says to open a device there, and the child can do so and compute on it. Where the parent has the gl backend open, the child’s device comes from the other backends.

  • Peta::WebGPU batches: $gpu->batch(sub { ... }), or $gpu->begin and $gpu->commit, send any number of dispatches to the card in one go, and $pipeline->bind(\@buffers) gives an object run takes in place of the pipeline and its buffer list, so a recurring dispatch no longer binds its buffers each time. A write inside a batch takes effect in order with the dispatches around it, and $gpu->timed(sub { ... }) returns the nanoseconds the card spent on a batch. $gpu->features and $gpu->has_feature name the optional features of the device, which now has half-precision floats in shaders (enable f16;) and timestamp queries wherever the adapter does.

  • Peta::WebGPU buffers: $buffer->read($offset, $length) reads a part of a buffer, so fetching one number no longer copies the whole buffer back from the card. $buffer->write takes a PDL ndarray of type float, long or ulong as well as a byte string.

  • Peta::WebGPU memory: a buffer made with contents no longer takes twice its size on the card until the next dispatch; large contents go up in pieces. $buffer->destroy frees a buffer’s memory at once, $gpu->memory reports what the device has allocated, and Device->new(memory_limit => 90) turns running out of card memory into an Out of Memory exception at 90 percent of the driver’s budget. A program that frees buffers and at once makes new ones is no longer told the card is full while the freed memory is still on its way back, and an error message names the first failure, not one that followed from it.

  • Peta::WebGPU on OpenGL: Device->new(backend => 'gl') opens on the NVIDIA driver, where it failed with “Parent device is lost”. Drawing works there; compute needs OpenGL 4.3 or ES 3.1, and on an older context dies with a message that names the vulkan backend.

  • Net::SSLeay version constants: OPENSSL_VERSION_MAJOR, OPENSSL_VERSION_MINOR, OPENSSL_VERSION_PATCH and OPENSSL_VERSION_NUMBER name the OpenSSL that is loaded. They kept the values of the release pperl was built beside, so after a system update to OpenSSL 3.6.5 OPENSSL_VERSION_PATCH still said 4.

  • GD has the rest of GD 2.91: the affine matrices and transforms, colorReplace and its variants, convolution, resolution, the transposed, rotated, scaled and blurred copies, animated GIF, HEIF and AVIF, 48 functions in all. GD::VERSION_STRING is now libgd’s version string and GD::LIBGD_VERSION its number (2.0303 for libgd 2.3.3), as in GD itself. GD::Image->supported and the newFrom<Format> methods follow what the installed libgd can read.

  • GD colorsTotal: $image->colorsTotal on a truecolor image returns undef, as in GD itself. It returned 0.

  • Config::config_vars and -V print the way Perl’s print does: to the handle select chose, through a tied STDOUT, and in order with the program’s other output. They wrote to the process’s standard output directly, so a capture saw nothing and, on a pipe, their lines could come out ahead of text printed earlier. Config::compile_date and the matching line of -V read Compiled at Oct  8 2026 20:42:44, the layout Config::Perl::V parses; they named the compiler instead.

  • Cairo, Cwd, Term::ReadLine::Gnu: Cairo::HAS_PS_SURFACE, HAS_PDF_SURFACE, HAS_SVG_SURFACE, HAS_RECORDING_SURFACE and HAS_FT_FONT exist. Cwd::CLONE exists. rl_save_state returns a readline_state_tPtr object, which is freed with its last reference and is the only thing rl_restore_state accepts.