Json fieldparentptr of a bitfield

I am insanely impressed by the json stuff I am fooling around with.
Only had to write 3 jsonStringify functions for my outlandish packed unions.
Two questions:

  1. How can I get the (Move) @FieldParentPtr of the below info field?

  2. I wrote my functions as pub fn jsonStringify(self: XXX, jws: *std.json.Stringify) Wouldn’t that not be more clear than pub fn jsonStringify(self: XXX, jws: anytype)?

const Move = packed struct(u16) {
    from: u6, 
    to: u6, 
    movetype: u2, 
    info: u2, // this one!
}

Here my chessboard :slight_smile:

{
  "layout": {
    "start_files": [
      0,
      7,
      4
    ],
    "king_start_squares": [
      "e1",
      "e8"
    ],
    "rook_start_squares": [
      [
        "h1",
        "a1"
      ],
      [
        "h8",
        "a8"
      ]
    ],
    "castling_between_bitboards": [
      [
        96,
        14
      ],
      [
        6917529027641081856,
        1008806316530991104
      ]
    ],
    "castling_king_paths": [
      [
        96,
        12
      ],
      [
        6917529027641081856,
        864691128455135232
      ]
    ],
    "castling_masks": [
      2,
      0,
      0,
      0,
      3,
      0,
      0,
      1,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      8,
      0,
      0,
      0,
      12,
      0,
      0,
      4
    ]
  },
  "board": [
    "no_piece",
    "no_piece",
    "no_piece",
    "w_rook",
    "w_rook",
    "no_piece",
    "no_piece",
    "w_king",
    "no_piece",
    "w_pawn",
    "w_pawn",
    "no_piece",
    "no_piece",
    "no_piece",
    "w_pawn",
    "no_piece",
    "w_pawn",
    "w_bishop",
    "w_knight",
    "no_piece",
    "no_piece",
    "w_pawn",
    "no_piece",
    "w_pawn",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "b_queen",
    "w_queen",
    "no_piece",
    "b_knight",
    "no_piece",
    "b_rook",
    "b_pawn",
    "b_pawn",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "b_pawn",
    "b_king",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "b_pawn",
    "b_rook",
    "no_piece",
    "b_bishop",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece",
    "no_piece"
  ],
  "bb_by_type": [
    396531910721357464,
    36102464308594176,
    137439215616,
    288230376151842816,
    72058143793741848,
    51539607552,
    140737488355456
  ],
  "bb_by_color": [
    34370700952,
    396531876350656512
  ],
  "values": [
    3314,
    3114
  ],
  "materials": [
    7452,
    7200
  ],
  "to_move": "black",
  "ply": 1,
  "game_ply": 47,
  "is_960": false,
  "state": {
    "rule50": 0,
    "ep_square": "a1",
    "castling_rights": 0,
    "last_move": {
      "from": "e5",
      "to": "d5",
      "type": "normal",
      "info": 0
    },
    "moved_piece": "w_queen",
    "captured_piece": "b_pawn",
    "key": 11146136902440991946,
    "checkers": 0,
    "checkmask": 0,
    "pins_diagonal": 0,
    "pins_orthogonal": 0,
    "pins": 0,
    "prev": {
      "rule50": 3,
      "ep_square": "a1",
      "castling_rights": 0,
      "last_move": {
        "from": "a1",
        "to": "a1",
        "type": "normal",
        "info": 0
      },
      "moved_piece": "no_piece",
      "captured_piece": "no_piece",
      "key": 7209347259727975727,
      "checkers": 0,
      "checkmask": 0,
      "pins_diagonal": 0,
      "pins_orthogonal": 551911718912,
      "pins": 551911718912,
      "prev": null
    }
  }
}

How can I get the (Move) @FieldParentPtr of the below info field?

const Move = packed struct(u16){
	from: u6,
	to: u6,
	movetype: u2,
	info: u2,
};

test Move {
	const move: Move = .{
		.from = 3,
		.to = 0,
		.movetype = 0,
		.info = 0,
	};
	const move_ptr: *Move = @constCast(@fieldParentPtr("info", &move.info));
	try std.testing.expect(move_ptr.from == 3);
}

I wrote my functions as pub fn jsonStringify(self: XXX, jws: *std.json.Stringify) Wouldn’t that not be more clear than pub fn jsonStringify(self: XXX, jws: anytype)?

Probably!
The “anytype” is a leftover from std.json.WriteStream which got removed from std.json in 0.15.1.
The std.json changes in 0.15.1 are seemingly rushed and incomplete, with a lot of documentation still referring to parts of the API that have been removed and aren’t seen in the documentation anymore.
So I wouldn’t be surprised if it becomes standard to have stringify functions take an explicit pointer to std.json.Stringify in future.

Aha! So I suspected already. I like typed.

In reality it is a bit more complicated.
The info: u2 is in reality const MoveInfo = packed union(u2) {...} with two “overlapping” u2 fields.

Depending on a field of the owning Move I have to output something.

pub fn jsonStringify(self: MoveInfo, jws: *std.json.Stringify)... {
    const m: *Move = @constCast(@fieldParentPtr("info", &self));
    // error: pointer bit-offset mismatch
    
}
const MoveInfo = packed union {
    a: Something,
    b: SomethingElse,
};

const Move = packed struct(u16){
	from: u6,
	to: u6,
	movetype: u2,
	info: MoveInfo, // this one
};

Yeah I have to stuff it all into 16 bits :slight_smile:

Try:

pub fn jsonStringify(self: *const MoveInfo, jws: *std.json.Stringify)... {
    const m: *const Move = @alignCast(@fieldParentPtr("info", &self));

but I am not sure if that alone will fix it, it depends whether your self pointer is actually correctly aligned. Your stringify function shouldn’t actually mutate anything.

Also I don’t feel like I completely understand bit alignment stuff and what you can/can’t do with it, partly because I haven’t really seen documentation about it.
Not sure whether *const MoveInfo would have to be declared with bit alignment.

I think the simplest thing to do would be to just write the jsonStringify function on the Move type in such a way that it doesn’t end up recursing into bit aligned types, but instead does everthing directly, so that it also does the formatting for all its fields including .info.

But maybe somebody more familiar with jsonStringify and packed types / bit alignments knows how to do it, without avoiding bit alignments.

Would be useful if you created a minimal example.

At a certain moment tyring a plethora of aligns and casts it compiled but crashes completely at runtime.
For now I skip it and just output the numeric value.